├── .clang-format ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── examples ├── ex1.c ├── ex1b.c ├── ex2.c ├── ex3.c ├── ex4.c ├── ex5.c ├── exfull.c ├── functions.c ├── supp.c └── tmpconst.h ├── src ├── ast.cc ├── ifswitch │ ├── README.md │ ├── ifswitch.cc │ └── ifswitch.h ├── initstruct │ ├── README.md │ ├── common.cc │ ├── global.cc │ ├── initstruct.h │ └── local.cc ├── macro.cc ├── main.cc ├── portcosmo.h ├── replace_int.cc ├── subcontext.cc ├── subcontext.h ├── utils.cc └── utils.h └── tmpconst.h /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: Google 3 | StatementMacros: 4 | - INITIALIZER 5 | AlignConsecutiveMacros: true 6 | AlignConsecutiveDeclarations: false 7 | AlwaysBreakBeforeMultilineStrings: false 8 | AllowShortFunctionsOnASingleLine: false 9 | KeepEmptyLinesAtTheStartOfBlocks: true 10 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 11 | --- 12 | Language: Cpp 13 | AllowShortFunctionsOnASingleLine: false 14 | --- 15 | Language: Proto 16 | ... 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | examples/* 3 | !examples/*.* 4 | *.pdf 5 | 6 | # Prerequisites 7 | *.d 8 | 9 | # Object files 10 | *.o 11 | *.ko 12 | *.obj 13 | *.elf 14 | 15 | # Linker output 16 | *.ilk 17 | *.map 18 | *.exp 19 | 20 | # Precompiled Headers 21 | *.gch 22 | *.pch 23 | 24 | # Libraries 25 | *.lib 26 | *.a 27 | *.la 28 | *.lo 29 | 30 | # Shared objects (inc. Windows DLLs) 31 | *.dll 32 | *.so 33 | *.so.* 34 | *.dylib 35 | 36 | # Executables 37 | *.exe 38 | *.out 39 | *.app 40 | *.i*86 41 | *.x86_64 42 | *.hex 43 | 44 | # Debug files 45 | *.dSYM/ 46 | *.su 47 | *.idb 48 | *.pdb 49 | 50 | # Kernel Module Compile Results 51 | *.mod* 52 | *.cmd 53 | .tmp_versions/ 54 | modules.order 55 | Module.symvers 56 | Mkfile.old 57 | dkms.conf 58 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | portcosmo - port software to Cosmopolitan Libc 4 | Copyright © 2022, Gautham Venkatasubramanian 5 | 6 | Permission to use, copy, modify, and/or distribute this software for any 7 | purpose with or without fee is hereby granted, provided that the above 8 | copyright notice and this permission notice appear in all copies. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 11 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 12 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 13 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 15 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 16 | PERFORMANCE OF THIS SOFTWARE. 17 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CXX = g++ 3 | CXX_PLUGIN_DIR = $(shell $(CXX) -print-file-name=plugin) 4 | 5 | ifeq ($(MODE),) 6 | PLUGIN_FLAGS = -I./src -I$(CXX_PLUGIN_DIR)/include -O2 -fno-rtti -fPIC -Wno-write-strings -DNDEBUG 7 | endif 8 | ifeq ($(MODE),dbg) 9 | PLUGIN_FLAGS = -I./src -I$(CXX_PLUGIN_DIR)/include -O2 -fno-rtti -fPIC -Wno-write-strings 10 | endif 11 | 12 | PLUGIN_SOURCES = $(wildcard src/*.cc) \ 13 | $(wildcard src/ifswitch/*.cc) \ 14 | $(wildcard src/initstruct/*.cc) 15 | PLUGIN_HEADERS = $(wildcard src/*.h) \ 16 | $(wildcard src/ifswitch/*.h) \ 17 | $(wildcard src/initstruct/*.h) 18 | PLUGIN_OBJS = $(PLUGIN_SOURCES:%.cc=%.o) 19 | PLUGIN_SONAME = ./portcosmo.so 20 | 21 | EXAMPLE_FLAGS = -I./examples -O2 22 | EXAMPLE_MODFLAGS = $(EXAMPLE_FLAGS) -fplugin=$(PLUGIN_SONAME) -DUSING_PLUGIN=1\ 23 | -include examples/tmpconst.h -include ./tmpconst.h 24 | EXAMPLE_RESFLAGS = $(EXAMPLE_FLAGS) -include examples/tmpconst.h -include ./tmpconst.h 25 | 26 | EXAMPLE_SOURCES = $(wildcard examples/ex*.c) 27 | EXAMPLE_RUNS = $(EXAMPLE_SOURCES:%.c=%.runs) 28 | EXAMPLE_MODBINS = $(EXAMPLE_SOURCES:examples/%.c=examples/modded_%) 29 | EXAMPLE_RESBINS = $(EXAMPLE_SOURCES:examples/%.c=examples/result_%) 30 | 31 | EXAMPLE_BINS = $(EXAMPLE_MODBINS) $(EXAMPLE_RESBINS) 32 | 33 | all: $(EXAMPLE_RUNS) $(EXAMPLE_BINS) 34 | 35 | $(EXAMPLE_RUNS): $(EXAMPLE_BINS) 36 | 37 | ./examples/%.runs: ./examples/modded_% ./examples/result_% 38 | diff -s <($(word 2,$^)) <($(word 1,$^)) 39 | 40 | ./examples/%.o: ./examples/%.c ./examples/tmpconst.h 41 | $(CC) $(EXAMPLE_FLAGS) $< -c -o $@ 42 | 43 | ./examples/modded_%.o: ./examples/%.c $(PLUGIN_SONAME) 44 | $(CC) $(EXAMPLE_MODFLAGS) $< -c -o $@ 45 | 46 | ./examples/modded_%: ./examples/modded_%.o ./examples/functions.o ./examples/supp.o 47 | $(CC) $^ -o $@ 48 | 49 | ./examples/result_%.o: ./examples/%.c $(PLUGIN_SONAME) 50 | $(CC) $(EXAMPLE_RESFLAGS) $< -c -o $@ 51 | 52 | ./examples/result_%: ./examples/result_%.o ./examples/functions.o 53 | $(CC) $^ -o $@ 54 | 55 | $(PLUGIN_SONAME): $(PLUGIN_OBJS) 56 | $(CXX) $^ -shared -o $@ 57 | 58 | ./src/%.o: ./src/%.cc $(PLUGIN_HEADERS) 59 | $(CXX) $(PLUGIN_FLAGS) $< -c -o $@ 60 | 61 | clean: 62 | rm -f ./examples/*.o 63 | rm -f $(EXAMPLE_BINS) 64 | rm -f $(EXAMPLE_RUNS) 65 | rm -f ./src/*.o ./src/ifswitch/*.o ./src/initstruct/*.o 66 | rm -f $(PLUGIN_SONAME) 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `portcosmo` - `gcc` plugin to help port software to Cosmopolitan Libc 2 | 3 | This is a `gcc` plugin written to ease porting C software to Cosmopolitan Libc. 4 | The general idea is to reduce manually changing the source code of any external 5 | software when attempting to build it with Cosmopolitan Libc -- ideally, you 6 | would need to customize only the build process, but make zero changes to the 7 | source code. 8 | 9 | Licensed under [ISC License](https://www.gnu.org/licenses/license-list.html#ISC). 10 | 11 | > I ended up [writing a `gcc` patch][gccpatch] with the code from this plugin. 12 | My patch is also licensed under ISC. The patched `gcc` does a lot less work than 13 | this plugin (and avoids almost all of the counterexamples) because I avoid using 14 | the macro hack and just patch the AST before the parser complains. (Per my 15 | current understanding, `gcc` does not provide plugin access to the program AST 16 | _during its construction in the parsing process_, which is why I wrote the patch 17 | instead. However, plugins provide a sufficiently large surface to figure out 18 | what a problem requires before diving into the depths of `gcc` internals. 19 | 20 | **Note**: this plugin has not yet been fully tested -- please check the compiled 21 | `.o` file, generated ASM, or errors in your test suite to confirm the 22 | correctness of the transformations. When in doubt, transform the code manually. 23 | See the [Counterexamples](#Counterexamples) section for more details. 24 | 25 | ## How to use this plugin? 26 | 27 | 1. Install the necessary `gcc` plugin headers (you need `gcc` to be able to use 28 | its plugin architecture) 29 | 2. Clone this repository and run `make` 30 | 3. Create a small shell script that uses `/usr/bin/gcc` with this plugin (ie add 31 | `-O2 -fplugin=/location/of/portcosmo.so -include /location/of/tmpconst.h`) 32 | and use that as `CC` when building software. 33 | 34 | For building software with Cosmopolitan Libc+this plugin, you will need to use 35 | [this branch](https://github.com/ahgamut/cosmopolitan/tree/symbolic-macro) where 36 | I've been trying to ensure I change as little of Cosmopolitan Libc as possible 37 | in order to make this work. And it does work! [This 38 | branch](https://github.com/ahgamut/cpython/tree/py311-custom) of CPython 39 | 3.11.0rc1 builds with Cosmopolitan Libc, and I didn't have to modify any 40 | `switch` statements. 41 | 42 | ## How does it work? 43 | 44 | Cosmopolitan Libc contains system-level constants (for example, errno constants 45 | like `SIGABRT`) defined as follows: 46 | 47 | ```c 48 | extern const int SIGABRT; 49 | #define SIGABRT ACTUALLY(SIGABRT) 50 | ``` 51 | 52 | This plugin activates upon finding a ` ACTUALLY(` (note the space) within a 53 | defined macro, and (re-)defines `ACTUALLY` as follows: 54 | 55 | ```c 56 | #define ACTUALLY(X) __tmpcosmo_##X 57 | ``` 58 | 59 | and records the location in the source file every time a macro containing 60 | `ACTUALLY(` is used. In `tmpconst.h`, there is a huge list of constants starting 61 | with the `__tmpcosmo_` prefix. 62 | 63 | After every (valid) macro usage has been recorded, this plugin walks through the 64 | entire AST of the source file to find each usage, and substitutes the 65 | appropriate `extern` variable name in the location where the macro was used. It 66 | does so via the below two components: 67 | 68 | * `ifswitch` -- rearrange `switch` statements if the case labels would otherwise 69 | raise the `case label is not constant` error. 70 | * `initstruct` -- update definitions of variables, `struct`s, and arrays if 71 | their initialization would otherwise raise the `initializer element is not 72 | constant` error (can handle `static` and global variables). 73 | 74 | The plugin errors out if the `ACTUALLY` macro was improperly used, or if it is 75 | unable to confirm all the macro usage records were substituted successfully. At 76 | the end of compilation, the plugin provides a note of how many substitutions 77 | were made when compiling the file. 78 | 79 | ## Why a compiler plugin? 80 | 81 | There might be other ways to check for such incorrect statements, but any 82 | method to rearrange these `switch` statements would need to incorporate a C 83 | preprocessor and parser, and any source code transformations would need to 84 | remain valid even if `ifdef`s are mixed within the C source code. 85 | 86 | Mixing `ifdef`s is a quite common occurrence in `switch` statements -- often 87 | times you see handlers for `errno` having a bunch of `ifdef`s (and 88 | fallthroughs!) to allow for different kinds of errno values based on the 89 | operating system. 90 | 91 | The best place to handle these statements is _after_ the preprocessor has done 92 | its work, so that the focus can be solely on the AST. `gcc` comes in with a 93 | battle-tested C preprocessor, parser, decent optimizations, and plugin support, 94 | so why not a `gcc` plugin? 95 | 96 | ## Counterexamples 97 | 98 | While this plugin can traverse through the code AST and modify almost all uses 99 | of the macro, there are a few cases where it may not be able to do so: 100 | 101 | * Using `gcc -O0` i.e. if you disable all optimizations, then `gcc` will not 102 | perform constant-folding and error out with `case label is not constant` with 103 | some source code like 104 | 105 | ``` 106 | case __tmpcosmo_SIGABRT: 107 | ``` 108 | 109 | This can likely be fixed, it's just a matter of enabling the right optimization 110 | flag in `gcc`. Better yet: we can figure out how to use `__tmpcosmo_SIGABRT` as 111 | a macro that can be defined during runtime, instead of a `static const int` in 112 | `tmpconst.h`, which would circumvent this problem. **Edit:** I ended up 113 | [patching `gcc`][gccpatch] with the code from this plugin, so this problem is avoided. 114 | 115 | * `case` labels with ranges, something like: 116 | 117 | ```c 118 | case SIGABRT .. 0: 119 | ``` 120 | 121 | Yes, I know it's possible to make this work, but I haven't seen any real-life C 122 | code that does something like this yet. 123 | 124 | * constant-folding algebra: 125 | 126 | ```c 127 | static const int e = SIGABRT; 128 | /* few lines later... */ 129 | func(e); 130 | ``` 131 | 132 | Under `gcc`'s optimization flags, `e` will be constant-folded, and its value 133 | will be used everywhere instead. The plugin has not recorded all the locations 134 | where `e` could have been used, so it just bails out when seeing a declaration 135 | like this. **Edit:** I ended up 136 | [patching `gcc`][gccpatch] with the code from this plugin, so this problem is avoided. 137 | 138 | ```c 139 | int x = SIGABRT+42; 140 | if(j < SIGABRT+42) 141 | case SIGABRT+42: 142 | for(int i=SIGABRT-1; i < 0; ++i) 143 | ``` 144 | 145 | Under `gcc`'s optimization flags, all of the above statements will have been 146 | constant-folded, and even though the plugins has recorded where the macro was 147 | used, it does not know what expression was simplified, so it bails out if it was 148 | unable to substitute a constant in any expression. **Edit:** I ended up 149 | [patching `gcc`][gccpatch] with the code from this plugin, so this problem is avoided. 150 | 151 | * magical things like Duff's device -- I don't know if any C code uses Duff's 152 | device with `SIGABRT`, would be fun to find out. **Edit:** I ended up 153 | [patching `gcc`][gccpatch] with the code from this plugin, so this problem is avoided. 154 | 155 | * substituting the incorrect location due to a `bad` pick of constant: Suppose 156 | we have some code which uses a lot of integer constants, and some of them *are 157 | on the same line* as when one of our macro substitutions was recorded, then 158 | the plugin will likely substitute the constant at the wrong location. See the 159 | below example: 160 | 161 | ```c 162 | /* suppose tmpconst.h has the below value */ 163 | static int __tmpcosmo_SIGABRT = -961; 164 | 165 | /* and your code has something like */ 166 | func(-961, SIGABRT); 167 | 168 | /* the macro will modify it to */ 169 | func(-961, __tmpcosmo_SIGABRT); 170 | /* and record the location of the modification */ 171 | 172 | /* but gcc will constant-fold it to */ 173 | func(-961, -961); 174 | 175 | /* the AST will be INCORRECTLY transformed into */ 176 | func(SIGABRT, -961); 177 | 178 | /* whereas the second param should actually be transformed */ 179 | func(-961, SIGABRT); 180 | ``` 181 | 182 | It might be possible to fix this via a hash-table of some sort, because we can 183 | just check the function call/expression at a marked location to confirm that it 184 | does *not* have the constant we just substituted anymore(ie our substitution 185 | actually fixed the macro use and some other constant in the source code). 186 | 187 | This can also be fixed if we had more precise location checking, at present, if 188 | your source code has a function call like 189 | 190 | ```c 191 | func(27, __tmpcosmo_SIGABRT); 192 | ``` 193 | 194 | In terms of line information, we only know that the `CALL_EXPR` with `func` 195 | starts on line 42 (and also its end sometimes) -- we do not know the location of 196 | the the individual parameters `27` and `-961`, which would be useful to match 197 | with the location we have saved from when the macro was used. 198 | 199 | > **Edit:** I ended up [patching `gcc`][gccpatch] with the code from this 200 | plugin, so this problem is avoided in *most* situations (I haven't found an 201 | example of this problem in real-life code yet). It can still happen if you're 202 | initializing a struct or writing a `switch` case with the clashing values, but 203 | my current belief is that the latter is quite rare (a `switch` whose options 204 | include both errno constants and other unrelated negative values), and the 205 | former is still uncommon, and would be caught by a simple test. Either way, the 206 | fix is the same as always: use different constants, or do the AST patching by 207 | hand. 208 | 209 | ## References: 210 | 211 | - The [`gcc` Internals documentation][gccint] -- this document, along with the 212 | `gcc` headers for plugin writers, provides everything you need to know about 213 | what plugins can do. 214 | - [History of C](https://en.cppreference.com/w/c/language/history) 215 | - [C99 `switch` constraints and semantics](https://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf) -- see page 92, $\S 6.8.4.2$ 216 | - [C11 `switch` constraints and semantics](https://open-std.org/JTC1/SC22/WG14/www/docs/n1570.pdf) -- see page 149, $\S 6.8.4.2$ 217 | - [C17 final draft `switch` constraints and semantics](https://files.lhmouse.com/standards/ISO%20C%20N2176.pdf) -- see page 108, $\S 6.8.4.2$ 218 | - [Assert Rewriting in `gcc`](https://jongy.github.io/2020/04/25/gcc-assert-introspect.html) 219 | - [An Introduction to `gcc` and `gcc`'s plugins](https://gabrieleserra.ml/blog/2020-08-27-an-introduction-to-gcc-and-gccs-plugins.html) 220 | - [LWN: Randomizing Structure Layout](https://lwn.net/Articles/722293/) 221 | - Source code of the 222 | [`randstruct`](https://github.com/torvalds/linux/blob/d37aa2efc89b387cda93bf15317883519683d435/scripts/gcc-plugins/randomize_layout_plugin.c) plugin used in the Linux kernel -- this is to understand how much can be done at the `gcc` plugin level. 223 | - [`gcc` OpenMP Runtime Wiki](https://gcc.gnu.org/wiki/openmp) -- need to 224 | understand how `pragma`s can be used to alert a plugin 225 | 226 | [gccpatch]: https://github.com/ahgamut/gcc/tree/portcosmo-11.2 227 | [gccint]: https://gcc.gnu.org/onlinedocs/gcc-11.2.0/gccint/ 228 | -------------------------------------------------------------------------------- /examples/ex1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* testing switch statement */ 5 | 6 | #define LOLMAX(X) case X: 7 | 8 | void exam_func(int value) { 9 | switch (value) { 10 | case 1: { 11 | // might create a variable in this scope 12 | int a = 21; 13 | something_1(); 14 | printf("you got a 1\n"); 15 | break; 16 | } 17 | 18 | #ifdef TWO 19 | LOLMAX(TWO) 20 | something_2(); 21 | printf("you got a 2\n"); 22 | // fall-through 23 | #endif 24 | 25 | case THREE: { 26 | int c = 22; 27 | something_3(); 28 | printf("you got a 3\n"); 29 | break; 30 | } 31 | 32 | case -THREE: 33 | printf("you got a -3\n"); 34 | break; 35 | 36 | case ~TWO: 37 | int d = 111; 38 | printf("you got a ~2\n"); 39 | break; 40 | 41 | case 19 ... 27: 42 | printf("sorry you don't get a number\n"); 43 | printf("hello TWO is %d\n", TWO); 44 | break; 45 | 46 | case 0: 47 | something_0(); 48 | // fall-through 49 | 50 | default: 51 | int z = 12; 52 | printf("default you got a %d\n", value); 53 | break; 54 | } 55 | printf("DONE WITH SWITCH\n"); 56 | printf("----------------\n"); 57 | } 58 | 59 | int main(int argc, char **argv) { 60 | exam_func(1); 61 | exam_func(2); 62 | exam_func(3); 63 | exam_func(0); 64 | exam_func(8); 65 | exam_func(22); 66 | exam_func(~TWO); 67 | exam_func(-THREE); 68 | return 0; 69 | } 70 | -------------------------------------------------------------------------------- /examples/ex1b.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* testing switch without default */ 5 | 6 | #define LOLMAX(X) case X: 7 | 8 | void exam_func(int value) { 9 | switch (value) { 10 | case 1: { 11 | // might create a variable in this scope 12 | int a = 21; 13 | something_1(); 14 | printf("you got a 1\n"); 15 | break; 16 | } 17 | 18 | #ifdef TWO 19 | LOLMAX(TWO) 20 | something_2(); 21 | printf("you got a 2\n"); 22 | // fall-through 23 | #endif 24 | 25 | case THREE: { 26 | int c = 22; 27 | something_3(); 28 | printf("you got a 3\n"); 29 | break; 30 | } 31 | 32 | case 19 ... 27: 33 | printf("sorry you don't get a number\n"); 34 | printf("hello TWO is %d\n", TWO); 35 | break; 36 | 37 | case 0: 38 | something_0(); 39 | // fall-through 40 | } 41 | printf("DONE WITH SWITCH\n"); 42 | printf("----------------\n"); 43 | } 44 | 45 | int main(int argc, char **argv) { 46 | exam_func(1); 47 | exam_func(0); 48 | exam_func(TWO); 49 | exam_func(22); 50 | exam_func(100); 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /examples/ex2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* testing initializations of global structs */ 5 | 6 | struct toy { 7 | int id; 8 | int value; 9 | }; 10 | 11 | struct toyroom { 12 | int id; 13 | int value; 14 | struct toy x1; 15 | struct toy x2; 16 | struct toy x3; 17 | struct toy x4; 18 | }; 19 | 20 | static int v1 = TWO; 21 | static int arr1[] = {TWO, THREE, ~THREE, ~TWO}; 22 | struct toy t0 = {.id = 30, .value = THREE}; 23 | static struct toy t1 = {.id = 31, .value = TWO}; 24 | static const struct toy ta[] = {{.id = 1, .value = ~TWO}, 25 | {.id = ~THREE, .value = TWO}, 26 | {.value = TWO, .id = -THREE}, 27 | {.id = THREE, .value = 1}, 28 | {.id = 7, .value = THREE}}; 29 | static const struct toyroom r1 = { 30 | .id = 2, 31 | .value = -TWO, 32 | .x1 = {.id = 1, .value = TWO}, 33 | .x2 = {.id = ~TWO, .value = 1}, 34 | .x3 = {.value = TWO, .id = -THREE}, 35 | .x4 = {.id = THREE, .value = ~THREE}, 36 | }; 37 | 38 | static __attribute__((constructor)) void myctor() { 39 | printf("v1 is %d\n", v1); 40 | printf("t0.id = %d, t0.value = %d\n", t1.id, t1.value); 41 | printf("t1.id = %d, t1.value = %d\n", t1.id, t1.value); 42 | for (int i = 0; i < 5; ++i) { 43 | printf("ta[%d].id = %d, ta[%d].value = %d\n", i, ta[i].id, i, ta[i].value); 44 | } 45 | printf("r1.id = %d, r1.value = %d\n", r1.id, r1.value); 46 | printf("r1.x1.id = %d, r1.x1.value = %d\n", r1.x1.id, r1.x1.value); 47 | printf("r1.x2.id = %d, r1.x2.value = %d\n", r1.x2.id, r1.x2.value); 48 | printf("r1.x3.id = %d, r1.x3.value = %d\n", r1.x3.id, r1.x3.value); 49 | printf("r1.x4.id = %d, r1.x4.value = %d\n", r1.x4.id, r1.x4.value); 50 | } 51 | static int okpls = 1; 52 | 53 | int main(int argc, char **argv) { 54 | printf("hi\n"); 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /examples/ex3.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* testing function calls */ 5 | 6 | int adder(int x, int y) { 7 | printf("x is %d, y is %d\n", x, y); 8 | return x + y + THREE; 9 | } 10 | 11 | int dummy(int x) { 12 | if (x == 1) { 13 | return ~TWO; 14 | } 15 | return -TWO; 16 | } 17 | 18 | int main(int argc, char **argv) { 19 | printf("hello TWO is %d\n", 20 | /* hello I want a comment here */ 21 | TWO); 22 | printf("dummy(1) = %d\n", dummy(1)); 23 | printf("dummy(2) = %d\n", dummy(2)); 24 | adder(1, TWO); 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /examples/ex4.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* testing initializations of local structs */ 5 | struct toy { 6 | int id; 7 | int value; 8 | }; 9 | 10 | struct toyroom { 11 | int id; 12 | int value; 13 | struct toy x1; 14 | struct toy x2; 15 | struct toy x3; 16 | struct toy x4; 17 | }; 18 | 19 | void init_func() { 20 | static const struct toy t = {.id = 22, 21 | /* OMG why do a comment here */ 22 | .value = TWO}; 23 | static int vals[] = {1, TWO, -THREE, ~TWO, 1}; 24 | 25 | printf("bye t.id = %d, t.value = %d\n", t.id, t.value); 26 | for (int i = 0; i < 5; ++i) { 27 | printf("vals[%d] = %d\n", i, vals[i]); 28 | vals[i] += 11; 29 | } 30 | 31 | struct toy box[] = { 32 | {.id = 1, .value = 1}, {.id = -THREE, .value = THREE}, 33 | {.id = THREE, .value = 1}, {.id = -TWO, .value = ~TWO}, 34 | {.id = TWO, .value = THREE}, {.id = ~THREE, .value = TWO}, 35 | }; 36 | for (int i = 0; i < 6; ++i) { 37 | printf("box[%d]: id=%d, value=%d\n", i, box[i].id, box[i].value); 38 | box[i].value -= i; 39 | } 40 | 41 | static const struct toyroom r2 = { 42 | .id = 2, 43 | .value = THREE, 44 | .x1 = {.id = 1, .value = TWO}, 45 | .x2 = {.id = ~TWO, .value = 1}, 46 | .x3 = {.id = TWO, .value = ~THREE}, 47 | .x4 = {.id = THREE, .value = -THREE}, 48 | }; 49 | 50 | for (int i = 0; i < TWO; i++) { 51 | printf("%d ", i); 52 | } 53 | printf("\n"); 54 | /* I can't mod the below expr because of constant folding */ 55 | /* 56 | for (int j = TWO-1; j > 0; j--) { 57 | printf("%d ", j); 58 | } 59 | */ 60 | static int LOL = /* 61 | commenting for posterity 62 | */ 63 | TWO; 64 | printf("LOL is %d\n", LOL); 65 | } 66 | 67 | int main(int argc, char **argv) { 68 | init_func(); 69 | init_func(); 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /examples/ex5.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* testing switch inside switch */ 5 | 6 | void exam_func(int value) { 7 | switch (value) { 8 | case 1: { 9 | int a = 21; 10 | something_1(); 11 | printf("you got a 1\n"); 12 | break; 13 | } 14 | 15 | case TWO: 16 | something_2(); 17 | printf("you got a 2\n"); 18 | break; 19 | 20 | default: 21 | int z = 12; 22 | printf("default you got a %d\n", value); 23 | switch (value) { 24 | case THREE: { 25 | int c = 22; 26 | something_3(); 27 | printf("you got a 3\n"); 28 | break; 29 | } 30 | 31 | case -THREE: 32 | printf("you got a -3\n"); 33 | break; 34 | 35 | case ~TWO: 36 | int d = 111; 37 | printf("you got a ~2\n"); 38 | break; 39 | 40 | case 19 ... 27: 41 | printf("sorry you don't get a number\n"); 42 | printf("hello TWO is %d\n", TWO); 43 | break; 44 | 45 | case 0: 46 | something_0(); 47 | // fall-through 48 | 49 | default: 50 | printf("default within default\n"); 51 | break; 52 | } 53 | break; 54 | } 55 | printf("DONE WITH SWITCH\n"); 56 | printf("----------------\n"); 57 | } 58 | 59 | int main(int argc, char **argv) { 60 | exam_func(1); 61 | exam_func(2); 62 | exam_func(3); 63 | exam_func(0); 64 | exam_func(8); 65 | exam_func(22); 66 | exam_func(~TWO); 67 | exam_func(-THREE); 68 | return 0; 69 | } 70 | -------------------------------------------------------------------------------- /examples/exfull.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* everything at once */ 5 | 6 | #define LOLMAX(X) case X: 7 | 8 | struct toy { 9 | int id; 10 | int value; 11 | }; 12 | 13 | struct toyroom { 14 | int id; 15 | int value; 16 | struct toy x1; 17 | struct toy x2; 18 | struct toy x3; 19 | struct toy x4; 20 | }; 21 | 22 | static int v1 = TWO; 23 | static int arr1[] = {TWO, THREE, ~THREE, ~TWO}; 24 | struct toy t0 = {.id = 30, .value = THREE}; 25 | static struct toy t1 = {.id = 31, .value = TWO}; 26 | static const struct toy ta[] = {{.id = 1, .value = ~TWO}, 27 | {.id = ~THREE, .value = TWO}, 28 | {.value = TWO, .id = -THREE}, 29 | {.id = THREE, .value = 1}, 30 | {.id = 7, .value = THREE}}; 31 | static const struct toyroom r1 = { 32 | .id = 2, 33 | .value = -TWO, 34 | .x1 = {.id = 1, .value = TWO}, 35 | .x2 = {.id = ~TWO, .value = 1}, 36 | .x3 = {.value = TWO, .id = -THREE}, 37 | .x4 = {.id = THREE, .value = ~THREE}, 38 | }; 39 | 40 | static __attribute__((constructor)) void myctor() { 41 | printf("v1 is %d\n", v1); 42 | printf("t0.id = %d, t0.value = %d\n", t0.id, t0.value); 43 | printf("t1.id = %d, t1.value = %d\n", t1.id, t1.value); 44 | t0.value += 112; 45 | t1.value += 27; 46 | for (int i = 0; i < 5; ++i) { 47 | printf("ta[%d].id = %d, ta[%d].value = %d\n", i, ta[i].id, i, ta[i].value); 48 | } 49 | printf("r1.id = %d, r1.value = %d\n", r1.id, r1.value); 50 | printf("r1.x1.id = %d, r1.x1.value = %d\n", r1.x1.id, r1.x1.value); 51 | printf("r1.x2.id = %d, r1.x2.value = %d\n", r1.x2.id, r1.x2.value); 52 | printf("r1.x3.id = %d, r1.x3.value = %d\n", r1.x3.id, r1.x3.value); 53 | printf("r1.x4.id = %d, r1.x4.value = %d\n", r1.x4.id, r1.x4.value); 54 | } 55 | static int okpls = 1; 56 | 57 | int adder(int x, int y) { 58 | printf("x is %d, y is %d\n", x, y); 59 | return x + y + THREE; 60 | } 61 | 62 | int dummy(int x) { 63 | if (x == 1) { 64 | return ~TWO; 65 | } 66 | return -TWO; 67 | } 68 | 69 | void exam_func(int value) { 70 | switch (value) { 71 | case 1: { 72 | // might create a variable in this scope 73 | something_1(); 74 | printf("you got a 1\n"); 75 | break; 76 | } 77 | 78 | #ifdef TWO 79 | LOLMAX(TWO) 80 | something_2(); 81 | printf("you got a 2\n"); 82 | // fall-through 83 | #endif 84 | 85 | case THREE: 86 | something_3(); 87 | printf("you got a 3\n"); 88 | break; 89 | 90 | case -THREE: 91 | printf("you got a -3\n"); 92 | break; 93 | 94 | case ~TWO: 95 | printf("you got a ~2\n"); 96 | break; 97 | 98 | case 19 ... 27: 99 | printf("sorry you don't get a number\n"); 100 | printf("hello TWO is %d\n", TWO); 101 | break; 102 | 103 | case 0: 104 | something_0(); 105 | // fall-through 106 | 107 | default: 108 | printf("default you got a %d\n", value); 109 | } 110 | printf("DONE WITH SWITCH\n"); 111 | printf("----------------\n"); 112 | } 113 | 114 | void init_func() { 115 | static const struct toy t = {.id = 22, 116 | /* OMG why do a comment here */ 117 | .value = TWO}; 118 | static int vals[] = {1, TWO, -THREE, ~TWO, 1}; 119 | 120 | printf("bye t.id = %d, t.value = %d\n", t.id, t.value); 121 | for (int i = 0; i < 5; ++i) { 122 | printf("vals[%d] = %d\n", i, vals[i]); 123 | } 124 | 125 | struct toy box[] = { 126 | {.id = 1, .value = 1}, {.id = -THREE, .value = THREE}, 127 | {.id = THREE, .value = 1}, {.id = -TWO, .value = ~TWO}, 128 | {.id = TWO, .value = THREE}, {.id = ~THREE, .value = TWO}, 129 | }; 130 | for (int i = 0; i < 6; ++i) { 131 | printf("box[%d]: id=%d, value=%d\n", i, box[i].id, box[i].value); 132 | } 133 | 134 | static const struct toyroom r2 = { 135 | .id = 2, 136 | .value = THREE, 137 | .x1 = {.id = 1, .value = TWO}, 138 | .x2 = {.id = ~TWO, .value = 1}, 139 | .x3 = {.id = TWO, .value = ~THREE}, 140 | .x4 = {.id = THREE, .value = -THREE}, 141 | }; 142 | 143 | for (int i = 0; i < TWO; i++) { 144 | printf("%d ", i); 145 | } 146 | printf("\n"); 147 | /* I can't mod the below expr because of constant folding */ 148 | /* 149 | for (int j = TWO-1; j > 0; j--) { 150 | printf("%d ", j); 151 | } 152 | */ 153 | static int LOL = /* 154 | commenting for posterity 155 | */ 156 | TWO; 157 | printf("LOL is %d\n", LOL); 158 | } 159 | 160 | int main(int argc, char **argv) { 161 | /* printf("This is the modded example\n"); */ 162 | printf("hello TWO is %d\n", 163 | /* hello I want a comment here */ 164 | TWO); 165 | exam_func(1); 166 | exam_func(2); 167 | exam_func(3); 168 | exam_func(0); 169 | exam_func(8); 170 | exam_func(22); 171 | exam_func(~TWO); 172 | exam_func(-THREE); 173 | init_func(); 174 | printf("dummy(1) = %d\n", dummy(1)); 175 | printf("dummy(2) = %d\n", dummy(2)); 176 | adder(1, TWO); 177 | myctor(); 178 | /* adder(-420, TWO); this is bad */ 179 | return 0; 180 | } 181 | -------------------------------------------------------------------------------- /examples/functions.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void something_0() { 4 | printf("something related to zero\n"); 5 | } 6 | 7 | void something_1() { 8 | printf("something related to one\n"); 9 | } 10 | 11 | void something_2() { 12 | printf("something related to two, two\n"); 13 | } 14 | 15 | void something_3() { 16 | printf("something related to three, three, three\n"); 17 | } 18 | -------------------------------------------------------------------------------- /examples/supp.c: -------------------------------------------------------------------------------- 1 | const int TWO = 17; 2 | const int THREE = 11; 3 | -------------------------------------------------------------------------------- /examples/tmpconst.h: -------------------------------------------------------------------------------- 1 | extern void something_0(); 2 | extern void something_1(); 3 | extern void something_2(); 4 | extern void something_3(); 5 | 6 | #ifdef USING_PLUGIN 7 | #ifndef ACTUALLY 8 | #define ACTUALLY(X) X 9 | #endif 10 | 11 | static const int __tmpcosmo_TWO = -420; 12 | static const int __tmpcosmo_THREE = -422; 13 | 14 | extern const int TWO; 15 | extern const int THREE; 16 | #define TWO ACTUALLY(TWO) 17 | #define THREE ACTUALLY(THREE) 18 | 19 | #else 20 | 21 | #define TWO 17 22 | #define THREE 11 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/ast.cc: -------------------------------------------------------------------------------- 1 | /*- mode:c++;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ 2 | │vi: set net ft=c++ ts=2 sts=2 sw=2 fenc=utf-8 :vi│ 3 | ╞══════════════════════════════════════════════════════════════════════════════╡ 4 | │ Copyright © 2022, Gautham Venkatasubramanian │ 5 | │ │ 6 | │ Permission to use, copy, modify, and/or distribute this software for │ 7 | │ any purpose with or without fee is hereby granted, provided that the │ 8 | │ above copyright notice and this permission notice appear in all copies. │ 9 | │ │ 10 | │ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ 11 | │ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ 12 | │ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ 13 | │ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ 14 | │ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ 15 | │ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ 16 | │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ 17 | │ PERFORMANCE OF THIS SOFTWARE. │ 18 | ╚─────────────────────────────────────────────────────────────────────────────*/ 19 | #include 20 | 21 | int check_expr(tree *tp, SubContext *ctx, location_t loc, source_range rng) { 22 | tree t = *tp; 23 | subu_node *use = NULL; 24 | tree replacement = NULL_TREE; 25 | int found = 0; 26 | 27 | if (TREE_CODE(t) == DECL_EXPR) { 28 | get_subu_elem(ctx->mods, loc, &use) || get_subu_elem2(ctx->mods, rng, &use); 29 | if (build_modded_int_declaration(tp, ctx, use)) { 30 | use = NULL; 31 | ctx->initcount += 1; 32 | DEBUGF("fixed\n"); 33 | ctx->prev = NULL; 34 | found = 1; 35 | } 36 | } else if (TREE_CODE(t) == BIND_EXPR) { 37 | // debug_tree(t); 38 | auto body = BIND_EXPR_BODY(t); 39 | if (EXPR_P(body) && check_expr(&body, ctx, loc, rng)) { 40 | found += 1; 41 | } else if (TREE_CODE(body) == STATEMENT_LIST) { 42 | for (auto it = tsi_start(body); !tsi_end_p(it); tsi_next(&it)) { 43 | auto tp2 = tsi_stmt_ptr(it); 44 | if (EXPR_P(*tp2) && check_expr(tp2, ctx, loc, rng)) { 45 | found += 1; 46 | } 47 | } 48 | } 49 | // debug_tree(t); 50 | } else if (TREE_CODE(t) == CALL_EXPR) { 51 | call_expr_arg_iterator it; 52 | tree arg = NULL_TREE; 53 | while (get_subu_elem(ctx->mods, loc, &use) || 54 | get_subu_elem2(ctx->mods, rng, &use)) { 55 | int i = 0; 56 | int rep = -1; 57 | FOR_EACH_CALL_EXPR_ARG(arg, it, t) { 58 | DEBUGF("arg %d is %s (%s)\n", i, get_tree_code_str(arg), use->name); 59 | // debug_tree(arg); 60 | if (arg_should_be_modded(arg, use, &replacement)) { 61 | DEBUGF("yup this is the constant we want\n"); 62 | CALL_EXPR_ARG(t, i) = replacement; 63 | remove_subu_elem(ctx->mods, use); 64 | use = NULL; 65 | ctx->subcount += 1; 66 | rep = i; 67 | found += 1; 68 | replacement = NULL_TREE; 69 | break; 70 | } else if (EXPR_P(arg) && check_expr(&arg, ctx, loc, rng)) { 71 | use = NULL; 72 | found += 1; 73 | rep = i; 74 | break; 75 | } 76 | i += 1; 77 | } 78 | if (rep == -1) break; 79 | } 80 | /* TODO: did we substitute something we weren't 81 | * supposed to substitute? check via hash-set? */ 82 | } else { 83 | DEBUGF("this contains a substitution and it is... a %s?\n", 84 | get_tree_code_str(t)); 85 | DEBUGF("how many operands do you have? %d\n", TREE_OPERAND_LENGTH(t)); 86 | // debug_tree(t); 87 | tree arg = NULL_TREE; 88 | int n = TREE_OPERAND_LENGTH(t); 89 | while (get_subu_elem(ctx->mods, loc, &use) || 90 | get_subu_elem2(ctx->mods, rng, &use)) { 91 | int i = 0; 92 | int rep = -1; 93 | for (i = 0; i < n; ++i) { 94 | arg = TREE_OPERAND(t, i); 95 | if (!arg || arg == NULL_TREE) continue; 96 | DEBUGF("arg %d is %s (%s)\n", i, get_tree_code_str(arg), use->name); 97 | // debug_tree(arg); 98 | if (arg_should_be_modded(arg, use, &replacement)) { 99 | DEBUGF("yup this is the constant we want\n"); 100 | rep = i; 101 | TREE_OPERAND(t, i) = replacement; 102 | remove_subu_elem(ctx->mods, use); 103 | use = NULL; 104 | ctx->subcount += 1; 105 | found += 1; 106 | replacement = NULL_TREE; 107 | break; 108 | } else if (EXPR_P(arg) && check_expr(&arg, ctx, loc, rng)) { 109 | use = NULL; 110 | found += 1; 111 | rep = i; 112 | break; 113 | } 114 | } 115 | if (rep == -1) break; 116 | } 117 | /* TODO: did we substitute something we weren't 118 | * supposed to substitute? check via hash-set? */ 119 | } 120 | return found; 121 | } 122 | 123 | tree check_usage(tree *tp, int *check_subtree, void *data) { 124 | SubContext *ctx = (SubContext *)(data); 125 | tree t = *tp; 126 | tree z; 127 | subu_node *use = NULL; 128 | location_t loc = EXPR_LOCATION(t); 129 | source_range rng = EXPR_LOCATION_RANGE(t); 130 | 131 | if (ctx->active == 0 || ctx->mods->count == 0) { 132 | /* DEBUGF("substitutions complete\n"); */ 133 | *check_subtree = 0; 134 | return NULL_TREE; 135 | } 136 | 137 | if (LOCATION_AFTER2(loc, rng.m_start)) { 138 | loc = rng.m_start; 139 | } else { 140 | rng.m_start = loc; 141 | } 142 | 143 | if (ctx->prev && LOCATION_BEFORE2(ctx->mods->start, rng.m_start)) { 144 | auto vloc = DECL_SOURCE_LOCATION(DECL_EXPR_DECL(*(ctx->prev))); 145 | /* below inequality holds inside this if condition: 146 | * vloc <= ctx->mods->start <= rng.m_start 147 | * this means that there was a macro substitution 148 | * between vloc and rng.m_start, which was not 149 | * eliminated when we went through the other parts 150 | * of the parse tree earlier. thus, the decl_expr 151 | * that we have stored in ctx->prev needs to be 152 | * checked for possible macro substitutions */ 153 | DEBUGF("did we miss a decl? vloc=%u,%u, loc=%u,%u, rng.mstart=%u,%u, " 154 | "start=%u,%u\n", 155 | LOCATION_LINE(vloc), LOCATION_COLUMN(vloc), // 156 | LOCATION_LINE(loc), LOCATION_COLUMN(loc), // 157 | LOCATION_LINE(rng.m_start), LOCATION_COLUMN(rng.m_start), 158 | LOCATION_LINE(ctx->mods->start), LOCATION_COLUMN(ctx->mods->start)); 159 | auto z = ctx->initcount; 160 | build_modded_declaration(ctx->prev, ctx, rng.m_start); 161 | if (z != ctx->initcount) { 162 | ctx->prev = NULL; 163 | check_context_clear(ctx, loc); 164 | } 165 | } 166 | 167 | if (TREE_CODE(t) == SWITCH_STMT) { 168 | rng = get_switch_bounds(t); 169 | if (valid_subu_bounds(ctx->mods, rng.m_start, rng.m_finish) && 170 | count_mods_in_switch(t, ctx->mods) > 0) { 171 | /* this is one of the switch statements 172 | * where we modified a case label */ 173 | DEBUGF("modding the switch \n"); 174 | *tp = build_modded_switch_stmt(t, ctx); 175 | DEBUGF("we modded it??\n"); 176 | walk_tree_without_duplicates(tp, check_usage, ctx); 177 | /* due to the above call, I don't need to check 178 | * any subtrees from this current location */ 179 | *check_subtree = 0; 180 | ctx->switchcount += 1; 181 | return NULL_TREE; 182 | } 183 | } 184 | 185 | if (TREE_CODE(t) == FOR_STMT || TREE_CODE(t) == WHILE_STMT || 186 | TREE_CODE(t) == IF_STMT || TREE_CODE(t) == DO_STMT || 187 | TREE_CODE(t) == USING_STMT || TREE_CODE(t) == CLEANUP_STMT || 188 | TREE_CODE(t) == TRY_BLOCK || TREE_CODE(t) == HANDLER) { 189 | /* there's nothing to check in these statements, the walk will 190 | * anyway (UM....) go through their subtrees, which will have the 191 | * expressions we need to mod */ 192 | return NULL_TREE; 193 | } 194 | 195 | if (TREE_CODE(t) == DECL_EXPR) { 196 | DEBUGF("decl_expr at (%u,%u)-(%u,%u)\n", LOCATION_LINE(rng.m_start), 197 | LOCATION_COLUMN(rng.m_start), LOCATION_LINE(rng.m_finish), 198 | LOCATION_COLUMN(rng.m_finish)); 199 | 200 | auto code_type = TREE_CODE(TREE_TYPE(DECL_EXPR_DECL(t))); 201 | if (POINTER_TYPE == code_type) { 202 | return NULL_TREE; 203 | } 204 | // debug_tree(DECL_EXPR_DECL(t)); 205 | if ((RECORD_TYPE == code_type || ARRAY_TYPE == code_type) && 206 | DECL_INITIAL(DECL_EXPR_DECL(t)) != NULL_TREE) { 207 | // debug_tree(DECL_INITIAL(DECL_EXPR_DECL(t))); 208 | ctx->prev = tp; 209 | return NULL_TREE; 210 | } 211 | 212 | if (INTEGRAL_TYPE_P(TREE_TYPE(DECL_EXPR_DECL(t)))) { 213 | /* suppose we have recorded a macro use 214 | * related to this declaration, but then 215 | * somehow, we did not actually fix the 216 | * substituted value in the below code. 217 | * something's fishy: maybe there is a 218 | * multi-line comment between the name 219 | * of the variable and the actual value. 220 | * we'll check again before the next 221 | * element in the AST, just in case */ 222 | auto vloc = DECL_SOURCE_LOCATION(DECL_EXPR_DECL(t)); 223 | // debug_tree(t); 224 | if (DECL_INITIAL(DECL_EXPR_DECL(t)) != NULL_TREE && 225 | LOCATION_LINE(ctx->mods->start) - LOCATION_LINE(vloc) < 20) { 226 | /* if you have a multi-line comment of more than 20 lines 227 | * between a variable declaration and it's initial value, 228 | * then the plugin will fail. This deterministic failure is 229 | * better than crashing randomly because we tried to DECL_INITIAL 230 | * some variable which had no macro substitutions near it. 231 | * 232 | * TODO: can we get better location handling for comments? 233 | * that might help with initializers, although there is a 234 | * nastier alternative. */ 235 | DEBUGF("just in case %u-%u, %u-%u\n", LOCATION_LINE(ctx->mods->start), 236 | LOCATION_COLUMN(ctx->mods->start), LOCATION_LINE(ctx->mods->end), 237 | LOCATION_COLUMN(ctx->mods->end)); 238 | ctx->prev = tp; 239 | } 240 | } 241 | } 242 | 243 | if (valid_subu_bounds(ctx->mods, rng.m_start, rng.m_finish) || 244 | check_loc_in_bound(ctx->mods, loc)) { 245 | if (get_subu_elem(ctx->mods, loc, &use) || 246 | get_subu_elem2(ctx->mods, rng, &use)) { 247 | /* one of our macro substitutions 248 | * has been executed either at the location loc, 249 | * or within the range rng */ 250 | DEBUGF("found mark at %u,%u in a %s\n", LOCATION_LINE(loc), 251 | LOCATION_COLUMN(loc), get_tree_code_str(t)); 252 | check_expr(tp, ctx, loc, rng); 253 | return NULL_TREE; 254 | } 255 | DEBUGF("got in but no use %s at %u-%u\n", get_tree_code_str(t), 256 | LOCATION_LINE(rng.m_start), LOCATION_LINE(rng.m_finish)); 257 | 258 | // debug_tree(t); 259 | } 260 | return NULL_TREE; 261 | } 262 | 263 | void handle_pre_genericize(void *gcc_data, void *user_data) { 264 | tree t = (tree)gcc_data; 265 | SubContext *ctx = (SubContext *)user_data; 266 | tree t2; 267 | if (ctx->active && TREE_CODE(t) == FUNCTION_DECL && DECL_INITIAL(t) != NULL && 268 | TREE_STATIC(t)) { 269 | /* this function is defined within the file I'm processing */ 270 | if (ctx->mods->count == 0) { 271 | // DEBUGF("no substitutions were made in %s\n", IDENTIFIER_NAME(t)); 272 | return; 273 | } 274 | t2 = DECL_SAVED_TREE(t); 275 | ctx->prev = NULL; 276 | walk_tree_without_duplicates(&t2, check_usage, ctx); 277 | /* now at this stage, all uses of our macros have been 278 | * fixed, INCLUDING case labels. Let's confirm that: */ 279 | check_context_clear(ctx, MAX_LOCATION_T); 280 | } 281 | } 282 | -------------------------------------------------------------------------------- /src/ifswitch/README.md: -------------------------------------------------------------------------------- 1 | # `ifswitch` -- transform `switch` statements to `if` statements 2 | 3 | ## Outline of the problem: 4 | 5 | A typical `switch` statement in C goes something like this: 6 | 7 | ```c 8 | switch (value) { 9 | case 1: { 10 | // might create a variable in this scope 11 | something_1(); 12 | printf("you got a 1\n"); 13 | break; 14 | } 15 | 16 | case TWO: 17 | something_2(); 18 | printf("you got a 2\n"); 19 | // fall-through 20 | 21 | case THREE: 22 | something_3(); 23 | printf("you got a 3\n"); 24 | break; 25 | 26 | case 0: 27 | something_0(); 28 | // fall-through 29 | 30 | default: 31 | printf("you got a %d\n", value); 32 | } 33 | ``` 34 | 35 | Now the above statement will be considered valid by the compiler _only if_ all 36 | the case values are known at compile-time. So `TWO` and `THREE` have to be 37 | `#define`s or `static const` values within the same translation unit, otherwise 38 | `gcc` gives you an error like `case is not constant`. 39 | 40 | For example, if `TWO` and `THREE` were defined as: 41 | 42 | ```c 43 | extern const int TWO; // defined in other.c 44 | extern const int THREE; // defined in other.c 45 | ``` 46 | 47 | You would get the aforementioned `case constant` error. 48 | 49 | ## What does this plugin do? 50 | 51 | The goal of this plugin is find switch statements like the above example and 52 | transform them as follows: 53 | 54 | ```c 55 | // rest of your code before the switch is unchanged 56 | 57 | int __plugin_tmp = value; 58 | // decide type of __plugin_tmp based on cases 59 | 60 | if (1 == __plugin_tmp) goto __plugin_switch_case_1; 61 | else if (TWO == __plugin_tmp) goto __plugin_switch_case_TWO; 62 | else if (THREE == __plugin_tmp) goto __plugin_switch_case_THREE; 63 | else if (0 == __plugin_tmp) goto __plugin_switch_case_0; 64 | else goto __plugin_switch_default; 65 | 66 | { // switch usually adds a scope 67 | __plugin_switch_case_1: { 68 | // might create a variable in this scope 69 | something_1(); 70 | printf("you got a 1\n"); 71 | goto __plugin_switch_end; 72 | } 73 | 74 | __plugin_switch_case_TWO: 75 | something_2(); 76 | printf("you got a 2\n"); 77 | // fall-through 78 | 79 | __plugin_switch_case_THREE: 80 | something_3(); 81 | printf("you got a 3\n"); 82 | goto __plugin_switch_end; 83 | 84 | __plugin_switch_case_0: 85 | something_0(); 86 | // fall-through 87 | 88 | __plugin_switch_default: 89 | printf("you got a %d\n", __plugin_tmp); 90 | goto __plugin_switch_end; 91 | } 92 | __plugin_switch_end: ;; 93 | 94 | // rest of your code after the switch is unchanged 95 | ``` 96 | 97 | Note that we do _not_ want to allow arbitrary C expressions as `switch` cases, 98 | not even case ranges, -- we just want to allow case constants that are not known 99 | at compile time. 100 | 101 | ## Is this even valid/possible? 102 | 103 | This transformation depends on the following details: 104 | 105 | - [x] `gcc` plugins can access the type information of `case` values -- they're 106 | all just integer values (or `INTEGER_CST`s in AST-speak) 107 | - [x] `gcc` plugins can access the type of the input to `switch` -- this ends up 108 | not mattering as much, because I use `save_expr` instead of creating a 109 | separate temporary variable `__plugin_tmp` 110 | - [x] `gcc` plugins can determine if an AST subtree is a value, `const` variable, 111 | or an arbitrary expression -- yes, but sometimes locations are wacky, 112 | especially if we have multiline comments. 113 | - [x] `gcc` plugins allow modifying the AST (like `openmp` or `randstruct`) 114 | - [ ] `gcc` plugins allow modifying the AST _after_ the preprocessor has completed, 115 | but _before_ raising the `case constant` error to the user -- this is not 116 | possible 117 | - [ ] there are hopefully zero edge cases where this transformation 118 | breaks invariants (Duff's device or some whack C++ class where 119 | `operator=`, `operator==`, and `operator int()` are defined with 120 | side-effects) -- `save_expr` should be okay, remains to be tested 121 | 122 | ## Current design: analyze all and change only if necessary 123 | 124 | In this method, the plugin would go through each `switch` statement, and choose 125 | to rearrange only if any of the cases are not known constants. 126 | 127 | Advantage: requires no annotation or manual labor -- you can just specify the 128 | plugin in your build script wherever you call `gcc` and it would silently 129 | rewrite the AST where necessary. 130 | 131 | Disadvantage: It _may_ be slow to check every `case` in every `switch` (so far 132 | it hasn't been an issue). 133 | 134 | ## Alternative design: via a `pragma`/attribute 135 | 136 | This would require someone to annotate the necessary `switch` statements with 137 | something like 138 | 139 | ```c 140 | #pragma ifswitch rearrange 141 | switch(value) { 142 | // cases 143 | } 144 | ``` 145 | 146 | Advantage: requires human approval -- since the codebase has to be changed, we 147 | will know that the AST was rewritten because it was requested specifically by 148 | the progammer. 149 | 150 | Disadvantage: requires manual checks of the code -- you would need to compile 151 | your code, find out this error, then annotate the specific `switch` statement. 152 | Also toggling certain behaviors within the pragma is a bit weird. 153 | 154 | ## Alternative design: intercept the error and modify necessary AST 155 | 156 | In this method, the plugin would activate only if `gcc` was raising a `case 157 | constant` error with respect to a particular `switch` statement. 158 | 159 | Advantage: requires no manual labor, and we check/modify only those `switch` 160 | statements which actually need to be modified. 161 | 162 | Disadvantage: this is (likely) not possible to do -- the issue is that the AST 163 | after a parse error is incomplete, so any modifications are not possible. There 164 | is also a fear of the `case constant` error being raised due to some unrelated 165 | issue, and the plugin making things worse. 166 | -------------------------------------------------------------------------------- /src/ifswitch/ifswitch.cc: -------------------------------------------------------------------------------- 1 | /*- mode:c++;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ 2 | │vi: set net ft=c++ ts=2 sts=2 sw=2 fenc=utf-8 :vi│ 3 | ╞══════════════════════════════════════════════════════════════════════════════╡ 4 | │ Copyright © 2022, Gautham Venkatasubramanian │ 5 | │ │ 6 | │ Permission to use, copy, modify, and/or distribute this software for │ 7 | │ any purpose with or without fee is hereby granted, provided that the │ 8 | │ above copyright notice and this permission notice appear in all copies. │ 9 | │ │ 10 | │ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ 11 | │ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ 12 | │ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ 13 | │ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ 14 | │ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ 15 | │ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ 16 | │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ 17 | │ PERFORMANCE OF THIS SOFTWARE. │ 18 | ╚─────────────────────────────────────────────────────────────────────────────*/ 19 | #include 20 | 21 | static tree get_switch_body(tree swexpr) { 22 | auto body = SWITCH_STMT_BODY(swexpr); 23 | if (TREE_CODE(body) == BIND_EXPR) { 24 | body = BIND_EXPR_BODY(body); 25 | } 26 | return body; 27 | } 28 | 29 | source_range get_switch_bounds(tree sws) { 30 | auto body = get_switch_body(sws); 31 | source_range rng; 32 | rng.m_start = MAX_LOCATION_T; 33 | rng.m_finish = MAX_LOCATION_T; 34 | if (STATEMENT_LIST_HEAD(body) && STATEMENT_LIST_TAIL(body)) { 35 | /* otherwise this is an empty switch statement */ 36 | auto rng1 = EXPR_LOCATION_RANGE(STATEMENT_LIST_HEAD(body)->stmt); 37 | auto rng2 = EXPR_LOCATION_RANGE(STATEMENT_LIST_TAIL(body)->stmt); 38 | rng.m_start = rng1.m_start; 39 | rng.m_finish = rng2.m_finish; 40 | } 41 | return rng; 42 | } 43 | 44 | unsigned int count_mods_in_switch(tree swexpr, subu_list *list) { 45 | tree body = get_switch_body(swexpr); 46 | tree t = NULL_TREE; 47 | tree replacement = NULL_TREE; 48 | subu_node *use = NULL; 49 | unsigned int count = 0; 50 | for (auto i = tsi_start(body); !tsi_end_p(i); tsi_next(&i)) { 51 | t = tsi_stmt(i); 52 | if (TREE_CODE(t) == CASE_LABEL_EXPR) { 53 | if (get_subu_elem(list, EXPR_LOCATION(t), 54 | &use) /* on a line we substituted */ 55 | && CASE_LOW(t) != NULL_TREE /* not a x..y range */ 56 | && CASE_HIGH(t) == NULL_TREE /* not a default */ 57 | && arg_should_be_modded(CASE_LOW(t), use, &replacement) 58 | /* the case is the one we substituted */) { 59 | DEBUGF("we substituted a case label at %u,%u\n", EXPR_LOC_LINE(t), 60 | EXPR_LOC_COL(t)); 61 | // debug_tree(CASE_LOW(t)); 62 | count += 1; 63 | } 64 | } 65 | } 66 | return count; 67 | } 68 | 69 | tree build_modded_label(unsigned int swcount, const char *case_str, 70 | location_t loc = UNKNOWN_LOCATION) { 71 | char dest[STRING_BUFFER_SIZE] = {0}; 72 | snprintf(dest, sizeof(dest), "__tmpcosmo_%u_%s", swcount, case_str); 73 | tree lab = build_decl(loc, LABEL_DECL, get_identifier(dest), void_type_node); 74 | /* gcc's GIMPLE needs to know that this label 75 | * is within the current function declaration */ 76 | DECL_CONTEXT(lab) = current_function_decl; 77 | return build1(LABEL_EXPR, void_type_node, lab); 78 | } 79 | 80 | tree build_modded_exit_label(unsigned int swcount) { 81 | return build_modded_label(swcount, "__end"); 82 | } 83 | 84 | static inline tree build_modded_if_stmt(tree condition, tree then_clause, 85 | tree else_clause = NULL_TREE) { 86 | return build3(COND_EXPR, void_type_node, condition, then_clause, else_clause); 87 | } 88 | 89 | tree modded_case_label(tree t, unsigned int i, tree swcond, vec *&ifs, 90 | SubContext *ctx, tree *default_label) { 91 | // debug_tree(t); 92 | tree result; 93 | tree replacement = NULL_TREE; 94 | subu_node *use = NULL; 95 | char case_str[STRING_BUFFER_SIZE] = {0}; 96 | 97 | if (CASE_LOW(t) == NULL_TREE) { 98 | DEBUGF("default case\n"); 99 | /* default label of the switch case, needs to be last */ 100 | result = build_modded_label(ctx->switchcount, "__dflt", EXPR_LOCATION(t)); 101 | *default_label = result; 102 | } else if (CASE_LOW(t) != NULL_TREE && CASE_HIGH(t) == NULL_TREE) { 103 | /* a case label */ 104 | if (get_subu_elem(ctx->mods, EXPR_LOCATION(t), &use) 105 | /* the case is on a line we substituted */ 106 | && arg_should_be_modded(CASE_LOW(t), use, &replacement) 107 | /* the case value is the one we substituted */) { 108 | DEBUGF("modded case\n"); 109 | result = 110 | build_modded_label(ctx->switchcount, use->name, EXPR_LOCATION(t)); 111 | ifs->safe_push(build_modded_if_stmt( 112 | build2(EQ_EXPR, integer_type_node, swcond, replacement), 113 | build1(GOTO_EXPR, void_type_node, LABEL_EXPR_LABEL(result)))); 114 | remove_subu_elem(ctx->mods, use); 115 | replacement = NULL_TREE; 116 | } else { 117 | /* a case label that we didn't substitute */ 118 | DEBUGF("unmodded case\n"); 119 | snprintf(case_str, sizeof(case_str), "%lx_", i); 120 | result = build_modded_label(ctx->switchcount, case_str, EXPR_LOCATION(t)); 121 | ifs->safe_push(build_modded_if_stmt( 122 | build2(EQ_EXPR, integer_type_node, swcond, CASE_LOW(t)), 123 | build1(GOTO_EXPR, void_type_node, LABEL_EXPR_LABEL(result)))); 124 | } 125 | } else { 126 | DEBUGF("unmodded case range\n"); 127 | /* CASE_LOW(t) != NULL_TREE && CASE_HIGH(t) != NULL_TREE */ 128 | /* this is a case x .. y sort of range */ 129 | snprintf(case_str, sizeof(case_str), "%lx_", i); 130 | result = build_modded_label(ctx->switchcount, case_str, EXPR_LOCATION(t)); 131 | ifs->safe_push(build_modded_if_stmt( 132 | build2(TRUTH_ANDIF_EXPR, integer_type_node, 133 | build2(GE_EXPR, integer_type_node, swcond, CASE_LOW(t)), 134 | build2(LE_EXPR, integer_type_node, swcond, CASE_HIGH(t))), 135 | build1(GOTO_EXPR, void_type_node, LABEL_EXPR_LABEL(result)))); 136 | } 137 | return result; 138 | } 139 | 140 | tree build_modded_switch_stmt(tree swexpr, SubContext *ctx) { 141 | int case_count = 0, break_count = 0; 142 | int has_default = 0; 143 | 144 | tree swcond = save_expr(SWITCH_STMT_COND(swexpr)); 145 | tree swbody = get_switch_body(swexpr); 146 | tree *tp = NULL; 147 | char dest[STRING_BUFFER_SIZE] = {0}; 148 | 149 | vec *ifs; 150 | vec_alloc(ifs, 0); 151 | 152 | tree exit_label = build_modded_exit_label(ctx->switchcount); 153 | tree default_label = NULL_TREE; 154 | 155 | for (auto it = tsi_start(swbody); !tsi_end_p(it); tsi_next(&it)) { 156 | tp = tsi_stmt_ptr(it); 157 | if (TREE_CODE(*tp) == CASE_LABEL_EXPR) { 158 | case_count += 1; 159 | has_default = has_default || (CASE_LOW(*tp) == NULL_TREE); 160 | /* replace the case statement with a goto */ 161 | *tp = 162 | modded_case_label(*tp, case_count, swcond, ifs, ctx, &default_label); 163 | } else if (TREE_CODE(*tp) == BREAK_STMT) { 164 | break_count += 1; 165 | /* replace the break statement with a goto to the end */ 166 | *tp = build1(GOTO_EXPR, void_type_node, LABEL_EXPR_LABEL(exit_label)); 167 | } else if (TREE_CODE(*tp) == BIND_EXPR) { 168 | for (auto it2 = tsi_start(BIND_EXPR_BODY(*tp)); !tsi_end_p(it2); 169 | tsi_next(&it2)) { 170 | auto tp2 = tsi_stmt_ptr(it2); 171 | if (TREE_CODE(*tp2) == BREAK_STMT) { 172 | break_count += 1; 173 | /* replace the break statement with a goto to the end */ 174 | *tp2 = 175 | build1(GOTO_EXPR, void_type_node, LABEL_EXPR_LABEL(exit_label)); 176 | } 177 | } 178 | } 179 | } 180 | /* add all the if statements to the start of the switch body */ 181 | /* TODO: do we have to combine them via COND_EXPR_ELSE? why, 182 | * is it not possible to just them as a list one after the other? */ 183 | tree res; 184 | unsigned int zz = 0; 185 | if (ifs->length() > 0) { 186 | res = (*ifs)[0]; 187 | for (zz = 1; zz < ifs->length(); ++zz) { 188 | COND_EXPR_ELSE(res) = (*ifs)[zz]; 189 | res = (*ifs)[zz]; 190 | } 191 | /* if we have a valid default for the switch, 192 | * it should be the final else branch */ 193 | if (default_label && default_label != NULL_TREE) { 194 | COND_EXPR_ELSE(res) = 195 | build1(GOTO_EXPR, void_type_node, LABEL_EXPR_LABEL(default_label)); 196 | } else { 197 | /* if we don't have a default, then the final else branch 198 | * should just jump to after the switch */ 199 | COND_EXPR_ELSE(res) = 200 | build1(GOTO_EXPR, void_type_node, LABEL_EXPR_LABEL(exit_label)); 201 | } 202 | /* reset to the start of the if-else tree */ 203 | res = (*ifs)[0]; 204 | } else if (has_default && default_label != NULL_TREE) { 205 | /* this switch has only a default? ok... */ 206 | res = build1(GOTO_EXPR, void_type_node, LABEL_EXPR_LABEL(default_label)); 207 | } else { 208 | /* this switch has no cases, and no default?! */ 209 | warning_at(EXPR_LOCATION(swcond), 0, "switch without cases or default?"); 210 | res = build1(GOTO_EXPR, void_type_node, LABEL_EXPR_LABEL(exit_label)); 211 | } 212 | auto it = tsi_start(swbody); 213 | tsi_link_before(&it, res, TSI_SAME_STMT); 214 | tsi_link_before(&it, build_empty_stmt(UNKNOWN_LOCATION), TSI_SAME_STMT); 215 | 216 | /* add the 'outside' of the switch, ie the 'finally' 217 | * aka the target of the break statements, the 'exit_label', 218 | * to the end of the switch body */ 219 | append_to_statement_list(build_empty_stmt(UNKNOWN_LOCATION), &swbody); 220 | append_to_statement_list(exit_label, &swbody); 221 | append_to_statement_list(build_empty_stmt(UNKNOWN_LOCATION), &swbody); 222 | /* 223 | snprintf(dest, sizeof(dest), 224 | "above switch had %d cases replaced and %d breaks\n", case_count, 225 | break_count); 226 | append_to_statement_list(build_call_expr(VAR_NAME_AS_TREE("printf"), 1, 227 | BUILD_STRING_AS_TREE(dest)), 228 | &swbody); */ 229 | 230 | /* debug_tree(swbody); */ 231 | /* we are returning SWITCH_STMT_BODY(swexpr), 232 | * instead of just swbody, because sometimes, 233 | * SWITCH_STMT_BODY(swexpr) may be a BIND_EXPR 234 | * that has some scoping-related information. */ 235 | return SWITCH_STMT_BODY(swexpr); 236 | } 237 | -------------------------------------------------------------------------------- /src/ifswitch/ifswitch.h: -------------------------------------------------------------------------------- 1 | #ifndef IFSWITCH_H 2 | #define IFSWITCH_H 3 | #include 4 | /* gcc utils first */ 5 | #include 6 | 7 | source_range get_switch_bounds(tree); 8 | unsigned int count_mods_in_switch(tree, subu_list *); 9 | tree build_modded_switch_stmt(tree, SubContext *); 10 | 11 | #endif /* IFSWITCH_H */ 12 | -------------------------------------------------------------------------------- /src/initstruct/README.md: -------------------------------------------------------------------------------- 1 | # `initstruct` -- initialize data with proper constants 2 | 3 | ## Outline of the problem 4 | 5 | C software libraries typically store important values like `errno` constants in 6 | `static` variables, and in some cases a wrapper `struct` containing the 7 | necessary constant. You can see it here in Python's [`faulthandler.c`][pyfault], 8 | but let's take a simple example: 9 | 10 | ```c 11 | struct toy { 12 | int status; 13 | int value; 14 | }; 15 | 16 | /* in global scope */ 17 | static int errvalues[] = {SIGABRT, SIGILL, SIGFPE}; 18 | struct toy t1 = {.status = SIGABRT, .value = 24}; 19 | static struct toy t2 = {.status = SIGABRT, .value = 25}; 20 | static struct toy box[] = { 21 | {.status = SIGABRT, .value = 27}, 22 | {.status = SIGABRT, .value = 28}, 23 | {.value = 29, .status = SIGABRT}, 24 | }; 25 | 26 | int main() { 27 | static int status = SIGILL; 28 | struct toy m1 = {.status = SIGABRT, .value = 24}; 29 | static struct toy m2 = {.status = SIGABRT, .value = 25}; 30 | return 0; 31 | } 32 | ``` 33 | 34 | Now, the above code will only compile correctly if `SIGABRT`, `SIGILL` and 35 | `SIGFPE` are compile-time constants. But suppose these values are not known at 36 | compile-time, is it possible to initialize the `struct`s so that it uses the 37 | right values when the program is running? 38 | 39 | ## What does this plugin do? 40 | 41 | The goal of this plugin is to append a one-time check that initializes these 42 | declared structs with the right values. This is what happens to the above code: 43 | 44 | ```c 45 | struct toy { 46 | int status; 47 | int value; 48 | }; 49 | 50 | /* in global scope */ 51 | static int errvalues[] = {__tmpcosmo_SIGABRT, __tmpcosmo_SIGILL, __tmpcosmo_SIGFPE}; 52 | __attribute__((constructor)) __hidden_ctor1() { 53 | errvalues[0] = SIGABRT; 54 | errvalues[1] = SIGILL; 55 | errvalues[2] = SIGFPE; 56 | } 57 | struct toy t1 = {.status = __tmpcosmo_SIGABRT, .value = 24}; 58 | __attribute__((constructor)) __hidden_ctor2() { 59 | t1.status = SIGABRT; 60 | } 61 | static struct toy t2 = {.status = __tmpcosmo_SIGABRT, .value = 25}; 62 | __attribute__((constructor)) __hidden_ctor3() { 63 | t2.status = SIGABRT; 64 | } 65 | static struct toy box[] = { 66 | {.status = __tmpcosmo_SIGABRT, .value = 27}, 67 | {.status = __tmpcosmo_SIGABRT, .value = 28}, 68 | {.value = 29, .status = __tmpcosmo_SIGABRT}, 69 | }; 70 | __attribute__((constructor)) __hidden_ctor4() { 71 | box[0].status = SIGABRT; 72 | box[1].status = SIGABRT; 73 | box[2].status = SIGABRT; 74 | } 75 | 76 | int main() { 77 | static int status = __tmpcosmo_SIGILL; 78 | static uint8_t __chk1 = 0; 79 | if(__chk1 == 0) { 80 | status = SIGILL; 81 | __chk1 = 1; 82 | } 83 | /* for local structs, it just modifies the initializer */ 84 | struct toy m1 = {.status = SIGABRT, .value = 24}; 85 | static struct toy m2 = {.status = __tmpcosmo_SIGABRT, .value = 25}; 86 | static uint8_t __chk2 = 0; 87 | if(__chk2 == 0) { 88 | struct toy __tmpm2 = {.status = SIGABRT, .value = 25}; 89 | __chk1 = 1; 90 | __builtin_memcpy(&m2, &__tmpm2, sizeof(m2)); 91 | } 92 | return 0; 93 | } 94 | ``` 95 | 96 | in the global scope, we have `__attribute__((constructor))` with the highest 97 | priority, that run exactly once upon program startup. Within local function 98 | scope, we declare a temporary variable to check for the first-time 99 | initialization, that has , and then copy (or `__builtin_memcpy`) the values we'd 100 | like to the target. This should work even for nested `static` structs, 101 | struct-of-arrays, array-of-structs, etc. 102 | 103 | ### What about `static const`? 104 | 105 | The plugin will error out if attempting to modify a `static const int` or 106 | similar integral primitive type, and raise a warning for `static const struct` 107 | or similar compound type. 108 | 109 | ## Why are the `struct` initializations different for global and local? 110 | 111 | Um, I wrote the local `struct` initialization first, which was quite easy due to 112 | `__builtin_memcpy`. But somehow I kept getting compiler errors when attempting 113 | to use `__builtin_memcpy` for the global `struct`s, so I wrote the 114 | component-wise modifier instead. I think the method used for global variables is 115 | better (less stack allocations), so I might update the code to that later 116 | sometime. 117 | 118 | [pyfault]: https://github.com/ahgamut/cpython/blob/master/Modules/faulthandler.c#L66 119 | -------------------------------------------------------------------------------- /src/initstruct/common.cc: -------------------------------------------------------------------------------- 1 | /*- mode:c++;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ 2 | │vi: set net ft=c++ ts=2 sts=2 sw=2 fenc=utf-8 :vi│ 3 | ╞══════════════════════════════════════════════════════════════════════════════╡ 4 | │ Copyright © 2022, Gautham Venkatasubramanian │ 5 | │ │ 6 | │ Permission to use, copy, modify, and/or distribute this software for │ 7 | │ any purpose with or without fee is hereby granted, provided that the │ 8 | │ above copyright notice and this permission notice appear in all copies. │ 9 | │ │ 10 | │ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ 11 | │ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ 12 | │ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ 13 | │ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ 14 | │ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ 15 | │ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ 16 | │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ 17 | │ PERFORMANCE OF THIS SOFTWARE. │ 18 | ╚─────────────────────────────────────────────────────────────────────────────*/ 19 | #include 20 | 21 | tree access_at(tree obj, tree ind) { 22 | if (TREE_CODE(TREE_TYPE(obj)) == ARRAY_TYPE) { 23 | return build_array_ref(input_location, obj, ind); 24 | } 25 | return build_component_ref(input_location, obj, 26 | get_identifier(IDENTIFIER_NAME(ind)), 27 | DECL_SOURCE_LOCATION(ind)); 28 | } 29 | 30 | void set_values_based_on_ctor(tree ctor, subu_list *list, tree body, tree lhs, 31 | location_t bound) { 32 | subu_node *use = NULL; 33 | unsigned int iprev = 0; 34 | bool started = true; 35 | tree replacement = NULL_TREE; 36 | 37 | while (list->count > 0 && LOCATION_BEFORE2(list->start, bound)) { 38 | tree index = NULL_TREE; 39 | tree val = NULL_TREE; 40 | unsigned int i = 0; 41 | int found = 0; 42 | FOR_EACH_CONSTRUCTOR_ELT(CONSTRUCTOR_ELTS(ctor), i, index, val) { 43 | DEBUGF("value %u is %s\n", i, get_tree_code_str(val)); 44 | if (!started && i <= iprev) continue; 45 | if (TREE_CODE(val) == INTEGER_CST) { 46 | for (use = list->head; use; use = use->next) { 47 | found = arg_should_be_modded(val, use, &replacement); 48 | if (found) break; 49 | } 50 | if (found) { 51 | iprev = i; 52 | started = false; 53 | break; 54 | } 55 | } else if (TREE_CODE(val) == CONSTRUCTOR) { 56 | auto sub = access_at(lhs, index); 57 | // debug_tree(sub); 58 | set_values_based_on_ctor(val, list, body, sub, bound); 59 | use = NULL; /* might've gotten stomped */ 60 | if (list->count == 0) return; 61 | get_subu_elem(list, list->start, &use); 62 | } 63 | } 64 | if (found) { 65 | auto modexpr = build2(MODIFY_EXPR, TREE_TYPE(index), 66 | access_at(lhs, index), replacement); 67 | // debug_tree(modexpr); 68 | append_to_statement_list(modexpr, &body); 69 | remove_subu_elem(list, use); 70 | replacement = NULL_TREE; 71 | DEBUGF("found; %d left\n", list->count); 72 | } else { 73 | /* we did not find any (more) substitutions to fix */ 74 | DEBUGF("exiting; %d left\n", list->count); 75 | break; 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/initstruct/global.cc: -------------------------------------------------------------------------------- 1 | /*- mode:c++;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ 2 | │vi: set net ft=c++ ts=2 sts=2 sw=2 fenc=utf-8 :vi│ 3 | ╞══════════════════════════════════════════════════════════════════════════════╡ 4 | │ Copyright © 2022, Gautham Venkatasubramanian │ 5 | │ │ 6 | │ Permission to use, copy, modify, and/or distribute this software for │ 7 | │ any purpose with or without fee is hereby granted, provided that the │ 8 | │ above copyright notice and this permission notice appear in all copies. │ 9 | │ │ 10 | │ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ 11 | │ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ 12 | │ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ 13 | │ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ 14 | │ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ 15 | │ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ 16 | │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ 17 | │ PERFORMANCE OF THIS SOFTWARE. │ 18 | ╚─────────────────────────────────────────────────────────────────────────────*/ 19 | #include 20 | 21 | void update_global_decls(tree dcl, SubContext *ctx) { 22 | tree body = alloc_stmt_list(); 23 | subu_node *use = NULL; 24 | char chk[STRING_BUFFER_SIZE]; 25 | 26 | /* dcl, the global declaration we have is like these: 27 | * 28 | * static int foo = __tmpcosmo_VAR; 29 | * static struct toy myvalue = {.x=1, .y=__tmpcosmo_VAR}; 30 | * 31 | * we're going to add functions as follows: 32 | * 33 | * static int foo = __tmpcosmo_VAR; 34 | * __attribute__((constructor)) __hidden_ctor1() { 35 | * foo = VAR; 36 | * } 37 | * static struct toy myvalue = {.x=1, .y=__tmpcosmo_VAR}; 38 | * __attribute__((constructor)) __hidden_ctor2() { 39 | * myvalue.y = VAR; 40 | * } 41 | * 42 | * the modifier functions have the constructor attribute, 43 | * so it they run before anything uses the static. it 44 | * works recursively too: you can have a struct of structs, 45 | * an array of structs, whatever, and it will figure out 46 | * where the substitutions are and attempt to mod them. 47 | * 48 | * a unique constructor for each declaration. probably 49 | * we could have a common constructor for the entire 50 | * file, but that's left as an exercise for the reader. */ 51 | if (INTEGRAL_TYPE_P(TREE_TYPE(dcl)) && 52 | get_subu_elem(ctx->mods, ctx->mods->start, &use) && 53 | /* use is non-NULL if get_subu_elem succeeds */ 54 | check_magic_equal(DECL_INITIAL(dcl), use->name)) { 55 | if (TREE_READONLY(dcl)) { 56 | error_at(EXPR_LOCATION(dcl), "cannot substitute this constant\n"); 57 | /* actually I can, but the issue is if one of gcc's optimizations 58 | * perform constant folding(and they do), I don't know all the spots 59 | * where this variable has been folded, so I can't substitute there */ 60 | ctx->active = 0; 61 | return; 62 | } 63 | append_to_statement_list( 64 | build2(MODIFY_EXPR, void_type_node, dcl, VAR_NAME_AS_TREE(use->name)), 65 | &body); 66 | /* 67 | append_to_statement_list( 68 | build_call_expr(VAR_NAME_AS_TREE("printf"), 2, 69 | BUILD_STRING_AS_TREE("ctor initstruct %s\n"), 70 | BUILD_STRING_AS_TREE(IDENTIFIER_NAME(dcl))), 71 | &body); 72 | */ 73 | remove_subu_elem(ctx->mods, use); 74 | cgraph_build_static_cdtor('I', body, 0); 75 | } else if ((RECORD_TYPE == TREE_CODE(TREE_TYPE(dcl)) || 76 | ARRAY_TYPE == TREE_CODE(TREE_TYPE(dcl))) && 77 | DECL_INITIAL(dcl) != NULL_TREE) { 78 | if (TREE_READONLY(dcl)) { 79 | warning_at(DECL_SOURCE_LOCATION(dcl), 0, 80 | "not sure if modding const structs is good\n"); 81 | TREE_READONLY(dcl) = 0; 82 | } 83 | if (LOCATION_BEFORE2(ctx->mods->end, input_location)) { 84 | set_values_based_on_ctor(DECL_INITIAL(dcl), ctx->mods, body, dcl, 85 | input_location); 86 | } else { 87 | set_values_based_on_ctor(DECL_INITIAL(dcl), ctx->mods, body, dcl, 88 | ctx->mods->end); 89 | } 90 | /* 91 | append_to_statement_list( 92 | build_call_expr(VAR_NAME_AS_TREE("printf"), 2, 93 | BUILD_STRING_AS_TREE("ctor initstruct %s\n"), 94 | BUILD_STRING_AS_TREE(IDENTIFIER_NAME(dcl))), 95 | &body); 96 | */ 97 | cgraph_build_static_cdtor('I', body, 0); 98 | DEBUGF("uploaded ctor\n"); 99 | } 100 | } 101 | 102 | void handle_decl(void *gcc_data, void *user_data) { 103 | tree t = (tree)gcc_data; 104 | SubContext *ctx = (SubContext *)user_data; 105 | if (ctx->active && ctx->mods->count > 0 && DECL_INITIAL(t) != NULL && 106 | DECL_CONTEXT(t) == NULL_TREE) { 107 | int internal_use = 108 | !strncmp(IDENTIFIER_NAME(t), "__tmpcosmo_", strlen("__tmpcosmo_")); 109 | if (internal_use || DECL_EXTERNAL(t)) { 110 | error_at(input_location, "the ACTUALLY is before the declaration!\n"); 111 | ctx->active = 0; 112 | return; 113 | } 114 | auto rng = EXPR_LOCATION_RANGE(t); 115 | rng.m_start = DECL_SOURCE_LOCATION(t); 116 | rng.m_finish = input_location; 117 | 118 | DEBUGF("handle_decl with %s %u,%u - %u-%u\n", IDENTIFIER_NAME(t), 119 | LOCATION_LINE(rng.m_start), LOCATION_COLUMN(rng.m_start), 120 | LOCATION_LINE(rng.m_finish), LOCATION_COLUMN(rng.m_finish)); 121 | ctx->initcount += ctx->mods->count; 122 | update_global_decls(t, ctx); 123 | /* now at this stage, all uses of our macros have been 124 | * fixed, INCLUDING case labels. Let's confirm that: */ 125 | check_context_clear(ctx, MAX_LOCATION_T); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/initstruct/initstruct.h: -------------------------------------------------------------------------------- 1 | #ifndef INITSTRUCT_H 2 | #define INITSTRUCT_H 3 | #include 4 | /* gcc utils first */ 5 | #include 6 | 7 | void build_modded_declaration(tree *, SubContext *, location_t); 8 | int build_modded_int_declaration(tree *, SubContext *, subu_node *); 9 | tree copy_struct_ctor(tree); 10 | void modify_local_struct_ctor(tree, subu_list *, location_t); 11 | 12 | void set_values_based_on_ctor(tree, subu_list *, tree, tree, location_t); 13 | void handle_decl(void *, void *); 14 | #endif /* INITSTRUCT_H */ 15 | -------------------------------------------------------------------------------- /src/initstruct/local.cc: -------------------------------------------------------------------------------- 1 | /*- mode:c++;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ 2 | │vi: set net ft=c++ ts=2 sts=2 sw=2 fenc=utf-8 :vi│ 3 | ╞══════════════════════════════════════════════════════════════════════════════╡ 4 | │ Copyright © 2022, Gautham Venkatasubramanian │ 5 | │ │ 6 | │ Permission to use, copy, modify, and/or distribute this software for │ 7 | │ any purpose with or without fee is hereby granted, provided that the │ 8 | │ above copyright notice and this permission notice appear in all copies. │ 9 | │ │ 10 | │ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ 11 | │ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ 12 | │ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ 13 | │ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ 14 | │ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ 15 | │ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ 16 | │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ 17 | │ PERFORMANCE OF THIS SOFTWARE. │ 18 | ╚─────────────────────────────────────────────────────────────────────────────*/ 19 | #include 20 | 21 | static inline tree build_modded_if_stmt(tree condition, tree then_clause, 22 | tree else_clause = NULL_TREE) { 23 | return build3(COND_EXPR, void_type_node, condition, then_clause, else_clause); 24 | } 25 | 26 | int build_modded_int_declaration(tree *dxpr, SubContext *ctx, subu_node *use) { 27 | char chk[STRING_BUFFER_SIZE]; 28 | tree dcl = DECL_EXPR_DECL(*dxpr); 29 | tree replacement = NULL_TREE; 30 | 31 | if (INTEGRAL_TYPE_P(TREE_TYPE(dcl)) && 32 | arg_should_be_modded(DECL_INITIAL(dcl), use, &replacement)) { 33 | if (TREE_READONLY(dcl)) { 34 | error_at(EXPR_LOCATION(dcl), "cannot substitute this constant\n"); 35 | /* actually I can, but the issue is if one of gcc's optimizations 36 | * perform constant folding(and they do), I don't know all the spots 37 | * where this variable has been folded, so I can't substitute there */ 38 | ctx->active = 0; 39 | return 0; 40 | } 41 | 42 | if (!TREE_STATIC(dcl)) { 43 | DECL_INITIAL(dcl) = replacement; 44 | remove_subu_elem(ctx->mods, use); 45 | replacement = NULL_TREE; 46 | return 1; 47 | } 48 | 49 | DEBUGF("fixing decl for a static integer\n"); 50 | /* (*dxpr), the statement we have is this: 51 | * 52 | * static int myvalue = __tmpcosmo_VAR; 53 | * 54 | * we're going to modify it to this: 55 | * 56 | * static int myvalue = __tmpcosmo_VAR; 57 | * static uint8 __chk_ifs_myvalue = 0; 58 | * if(__chk_ifs_myvalue != 1) { 59 | * __chk_ifs_myvalue = 1; 60 | * myvalue = VAR; 61 | * } 62 | * 63 | * so the modified statement runs exactly once, 64 | * whenever the function is first called, right 65 | * after the initialization of the variable we 66 | * wanted to modify. */ 67 | 68 | /* build __chk_ifs_myvalue */ 69 | snprintf(chk, sizeof(chk), "__chk_ifs_%s", IDENTIFIER_NAME(dcl)); 70 | tree chknode = build_decl(DECL_SOURCE_LOCATION(dcl), VAR_DECL, 71 | get_identifier(chk), uint8_type_node); 72 | DECL_INITIAL(chknode) = build_int_cst(uint8_type_node, 0); 73 | TREE_STATIC(chknode) = TREE_STATIC(dcl); 74 | TREE_USED(chknode) = TREE_USED(dcl); 75 | DECL_READ_P(chknode) = DECL_READ_P(dcl); 76 | DECL_CONTEXT(chknode) = DECL_CONTEXT(dcl); 77 | DECL_CHAIN(chknode) = DECL_CHAIN(dcl); 78 | DECL_CHAIN(dcl) = chknode; 79 | 80 | /* create the then clause of the if statement */ 81 | tree then_clause = alloc_stmt_list(); 82 | append_to_statement_list(build2(MODIFY_EXPR, void_type_node, chknode, 83 | build_int_cst(uint8_type_node, 1)), 84 | &then_clause); 85 | append_to_statement_list( 86 | build2(MODIFY_EXPR, void_type_node, dcl, replacement), 87 | &then_clause); 88 | /* 89 | append_to_statement_list( 90 | build_call_expr(VAR_NAME_AS_TREE("printf"), 1, 91 | BUILD_STRING_AS_TREE("initstruct magic\n")), 92 | &then_clause); 93 | */ 94 | 95 | /* create the if statement into the overall result mentioned above */ 96 | tree res = alloc_stmt_list(); 97 | append_to_statement_list(*dxpr, &res); 98 | append_to_statement_list(build1(DECL_EXPR, void_type_node, chknode), &res); 99 | append_to_statement_list( 100 | build_modded_if_stmt(build2(NE_EXPR, void_type_node, chknode, 101 | build_int_cst(uint8_type_node, 1)), 102 | then_clause), 103 | &res); 104 | /* overwrite the input tree with our new statements */ 105 | *dxpr = res; 106 | // debug_tree(res); 107 | remove_subu_elem(ctx->mods, use); 108 | replacement = NULL_TREE; 109 | return 1; 110 | } 111 | return 0; 112 | } 113 | 114 | void modify_local_struct_ctor(tree ctor, subu_list *list, location_t bound) { 115 | subu_node *use = NULL; 116 | unsigned int iprev = 0; 117 | bool started = true; 118 | tree replacement = NULL_TREE; 119 | 120 | while (list->count > 0 && LOCATION_BEFORE2(list->start, bound)) { 121 | tree val = NULL_TREE; 122 | unsigned int i = 0; 123 | int found = 0; 124 | FOR_EACH_CONSTRUCTOR_VALUE(CONSTRUCTOR_ELTS(ctor), i, val) { 125 | DEBUGF("value %u is %s\n", i, get_tree_code_str(val)); 126 | // debug_tree(val); 127 | if (TREE_CODE(val) == INTEGER_CST) { 128 | for (use = list->head; use; use = use->next) { 129 | found = arg_should_be_modded(val, use, &replacement); 130 | if (found) break; 131 | } 132 | if (found) { 133 | iprev = i; 134 | started = false; 135 | break; 136 | } 137 | } else if (TREE_CODE(val) == CONSTRUCTOR) { 138 | modify_local_struct_ctor(val, list, bound); 139 | use = NULL; /* might've gotten stomped */ 140 | if (list->count == 0 || LOCATION_AFTER2(list->start, bound)) return; 141 | } 142 | } 143 | if (found) { 144 | DEBUGF("found\n"); 145 | // debug_tree(CONSTRUCTOR_ELT(ctor, i)->index); 146 | CONSTRUCTOR_ELT(ctor, i)->value = replacement; 147 | // debug_tree(CONSTRUCTOR_ELT(ctor, i)->value); 148 | remove_subu_elem(list, use); 149 | replacement = NULL_TREE; 150 | } else { 151 | /* we did not find any (more) substitutions to fix */ 152 | break; 153 | } 154 | } 155 | } 156 | 157 | tree copy_struct_ctor(tree ctor) { 158 | tree ind = NULL_TREE; 159 | tree val = NULL_TREE; 160 | constructor_elt x = {.index = NULL, .value = NULL}; 161 | unsigned int i = 0; 162 | tree dupe = build0(CONSTRUCTOR, TREE_TYPE(ctor)); 163 | FOR_EACH_CONSTRUCTOR_ELT(CONSTRUCTOR_ELTS(ctor), i, ind, val) { 164 | x.index = copy_node(ind); 165 | if (TREE_CODE(val) == CONSTRUCTOR) { 166 | x.value = copy_struct_ctor(val); 167 | } else { 168 | x.value = copy_node(val); 169 | } 170 | vec_safe_push(((tree_constructor *)(dupe))->elts, x); 171 | } 172 | return dupe; 173 | } 174 | 175 | void build_modded_declaration(tree *dxpr, SubContext *ctx, location_t bound) { 176 | char chk[STRING_BUFFER_SIZE]; 177 | tree dcl = DECL_EXPR_DECL(*dxpr); 178 | subu_node *use = NULL; 179 | subu_list *list = ctx->mods; 180 | unsigned int oldcount = list->count; 181 | 182 | // debug_tree(DECL_INITIAL(dcl)); 183 | 184 | if (INTEGRAL_TYPE_P(TREE_TYPE(dcl))) { 185 | get_subu_elem(list, list->start, &use); 186 | if (build_modded_int_declaration(dxpr, ctx, use)) { 187 | use = NULL; 188 | ctx->initcount += 1; 189 | } 190 | return; 191 | } 192 | 193 | if ((RECORD_TYPE == TREE_CODE(TREE_TYPE(dcl)) || 194 | ARRAY_TYPE == TREE_CODE(TREE_TYPE(dcl))) && 195 | DECL_INITIAL(dcl) != NULL_TREE) { 196 | if (TREE_READONLY(dcl)) { 197 | warning_at(EXPR_LOCATION(*dxpr), 0, 198 | "not sure if modding const structs is good\n"); 199 | TREE_READONLY(dcl) = 0; 200 | build_modded_declaration(dxpr, ctx, bound); 201 | return; 202 | } else if (TREE_STATIC(dcl)) { 203 | DEBUGF("fixing decl for a static struct\n"); 204 | /* (*dxpr), the statement we have is this: 205 | * 206 | * static struct toy myvalue = {.x=1, .y=__tmpcosmo_VAR}; 207 | * 208 | * we're going to modify it to this: 209 | * 210 | * static struct toy myvalue = {.x=1, .y=__tmpcosmo_VAR}; 211 | * static uint8 __chk_ifs_myvalue = 0; 212 | * if(__chk_ifs_myvalue != 1) { 213 | * __chk_ifs_myvalue = 1; 214 | * myvalue.y = VAR; 215 | * } 216 | * 217 | * so the modified statement runs exactly once, 218 | * whenever the function is first called, right 219 | * after the initialization of the variable we 220 | * wanted to modify. */ 221 | 222 | /* build __chk_ifs_myvalue */ 223 | snprintf(chk, sizeof(chk), "__chk_ifs_%s", IDENTIFIER_NAME(dcl)); 224 | tree chknode = build_decl(DECL_SOURCE_LOCATION(dcl), VAR_DECL, 225 | get_identifier(chk), uint8_type_node); 226 | DECL_INITIAL(chknode) = build_int_cst(uint8_type_node, 0); 227 | TREE_STATIC(chknode) = TREE_STATIC(dcl); 228 | TREE_USED(chknode) = TREE_USED(dcl); 229 | DECL_READ_P(chknode) = DECL_READ_P(dcl); 230 | DECL_CONTEXT(chknode) = DECL_CONTEXT(dcl); 231 | DECL_CHAIN(chknode) = DECL_CHAIN(dcl); 232 | DECL_CHAIN(dcl) = chknode; 233 | 234 | /* build a scope block for the temporary value */ 235 | tree tmpscope = build0(BLOCK, void_type_node); 236 | BLOCK_SUPERCONTEXT(tmpscope) = TREE_BLOCK(*dxpr); 237 | // debug_tree(BLOCK_SUPERCONTEXT(tmpscope)); 238 | 239 | /* create the then clause of the if statement */ 240 | tree then_clause = alloc_stmt_list(); 241 | append_to_statement_list(build2(MODIFY_EXPR, void_type_node, chknode, 242 | build_int_cst(uint8_type_node, 1)), 243 | &then_clause); 244 | set_values_based_on_ctor(DECL_INITIAL(dcl), ctx->mods, then_clause, dcl, 245 | bound); 246 | /* 247 | append_to_statement_list( 248 | build_call_expr(VAR_NAME_AS_TREE("printf"), 2, 249 | BUILD_STRING_AS_TREE("initstruct magic %lu bytes\n"), 250 | DECL_SIZE_UNIT(dcl)), 251 | &then_clause); 252 | */ 253 | 254 | /* create the if statement into the overall result mentioned above */ 255 | tree res = alloc_stmt_list(); 256 | append_to_statement_list(*dxpr, &res); 257 | append_to_statement_list(build1(DECL_EXPR, void_type_node, chknode), 258 | &res); 259 | append_to_statement_list( 260 | build_modded_if_stmt(build2(NE_EXPR, void_type_node, chknode, 261 | build_int_cst(uint8_type_node, 1)), 262 | then_clause), 263 | &res); 264 | /* overwrite the input tree with our new statements */ 265 | *dxpr = res; 266 | } else { 267 | /* if it's a local struct, we can 268 | * just mod the constructor itself */ 269 | auto ctor = DECL_INITIAL(dcl); 270 | modify_local_struct_ctor(ctor, list, bound); 271 | } 272 | } 273 | ctx->initcount += (oldcount - list->count); 274 | } 275 | -------------------------------------------------------------------------------- /src/macro.cc: -------------------------------------------------------------------------------- 1 | /*- mode:c++;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ 2 | │vi: set net ft=c++ ts=2 sts=2 sw=2 fenc=utf-8 :vi│ 3 | ╞══════════════════════════════════════════════════════════════════════════════╡ 4 | │ Copyright © 2022, Gautham Venkatasubramanian │ 5 | │ │ 6 | │ Permission to use, copy, modify, and/or distribute this software for │ 7 | │ any purpose with or without fee is hereby granted, provided that the │ 8 | │ above copyright notice and this permission notice appear in all copies. │ 9 | │ │ 10 | │ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ 11 | │ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ 12 | │ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ 13 | │ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ 14 | │ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ 15 | │ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ 16 | │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ 17 | │ PERFORMANCE OF THIS SOFTWARE. │ 18 | ╚─────────────────────────────────────────────────────────────────────────────*/ 19 | #include 20 | 21 | /* DON'T FREE THE PARTS OF THIS */ 22 | extern SubContext plugin_context; 23 | 24 | void check_macro_use(cpp_reader *reader, location_t loc, cpp_hashnode *node) { 25 | if (plugin_context.active != 1 || !cpp_user_macro_p(node)) return; 26 | if (LOCATION_COLUMN(loc) == 0) return; /* this is an ifdef? */ 27 | const char *defn = (const char *)cpp_macro_definition(reader, node); 28 | /* the definitions I am looking for are EXACTLY of the form 29 | * 30 | * #define X ACTUALLY(X) 31 | * ^^^^^^^^^^ ----> (test for this) 32 | * So the left-hand side of the macro (ie before the space) 33 | * should be the same as the argument inside the macro 34 | * (ie within the parentheses), otherwise this substitution 35 | * is most likely not worth recording, or an error. */ 36 | if (defn && strstr(defn, " ACTUALLY(") && !strstr(defn, "__tmpcosmo_")) { 37 | DEBUGF("at %u,%u checking macro.. %s\n", LOCATION_LINE(loc), 38 | LOCATION_COLUMN(loc), defn); 39 | const char *arg_start = strstr(defn, "("); 40 | const char *arg_end = strstr(defn, ")"); 41 | if (!arg_start || !arg_end || arg_end - arg_start < 1) return; 42 | arg_start += 1; /* move from '(' to start of arg */ 43 | if (strncmp(defn, arg_start, arg_end - arg_start) == 0) { 44 | /* This is most likely a substitution we need to 45 | * record, but I don't have enough info to say whether 46 | * this is a case label or just a normal statement 47 | * inside a switch, or something outside a switch */ 48 | add_context_subu(&plugin_context, loc, defn, (arg_end - arg_start), 49 | UNKNOWN); 50 | } else { 51 | cpp_error_at(reader, CPP_DL_ERROR, loc, "unable to check macro usage\n"); 52 | plugin_context.active = 0; 53 | } 54 | } 55 | } 56 | 57 | void check_macro_define(cpp_reader *reader, location_t loc, 58 | cpp_hashnode *node) { 59 | const char *defn = (const char *)cpp_macro_definition(reader, node); 60 | if (plugin_context.active == 0 && strstr(defn, " ACTUALLY(")) { 61 | plugin_context.active = 1; 62 | cpp_callbacks *cbs = cpp_get_callbacks(reader); 63 | if (cbs && cbs->used == NULL) { 64 | cbs->used = check_macro_use; 65 | } 66 | INFORM(loc, "recording usage of ACTUALLY() macro...\n"); 67 | } 68 | /* TODO: at this point in execution, is it possible to 69 | * 70 | * #define __tmpcosmo_X 71 | * 72 | * and use this instead of defining __tmp constants in 73 | * tmpconst.h? update a hash-table here, to use later in 74 | * lookups and substitution checks during the parsing. */ 75 | } 76 | 77 | void activate_macro_check(cpp_reader *reader = NULL) { 78 | if (!reader) { 79 | reader = parse_in; /* TODO: figure out a better way */ 80 | } 81 | cpp_callbacks *cbs = cpp_get_callbacks(reader); 82 | cpp_options *opts = cpp_get_options(reader); 83 | if (opts->lang == CLK_ASM) { 84 | inform(UNKNOWN_LOCATION, "plugin does not activate for ASM\n"); 85 | return; 86 | } 87 | cpp_undef(reader, "ACTUALLY"); 88 | cpp_define_formatted(reader, "ACTUALLY(X) = __tmpcosmo_ ## X"); 89 | if (cbs && cbs->define == NULL) { 90 | cbs->define = check_macro_define; 91 | } 92 | } 93 | 94 | void deactivate_macro_check(cpp_reader *reader = NULL) { 95 | if (!reader) { 96 | reader = parse_in; /* TODO: figure out a better way */ 97 | } 98 | cpp_callbacks *cbs = cpp_get_callbacks(reader); 99 | if (cbs && cbs->define == check_macro_define) { 100 | cbs->define = NULL; 101 | } 102 | if (cbs && cbs->used == check_macro_use) { 103 | cbs->used = NULL; 104 | } 105 | plugin_context.active = 0; 106 | cpp_undef(reader, "ACTUALLY"); 107 | cpp_define_formatted(reader, "ACTUALLY(X) = X"); 108 | } 109 | 110 | void handle_start_tu(void *gcc_data, void *user_data) { 111 | activate_macro_check(); 112 | } 113 | 114 | void handle_finish_tu(void *gcc_data, void *user_data) { 115 | DEBUGF("Attempting cleanup...\n"); 116 | SubContext *ctx = (SubContext *)user_data; 117 | if (ctx != &plugin_context) { 118 | fatal_error(MAX_LOCATION_T, "unable to clear plugin data!"); 119 | } else { 120 | check_context_clear(ctx, MAX_LOCATION_T); 121 | ctx->prev = NULL; 122 | } 123 | deactivate_macro_check(); 124 | } 125 | -------------------------------------------------------------------------------- /src/main.cc: -------------------------------------------------------------------------------- 1 | /*- mode:c++;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ 2 | │vi: set net ft=c++ ts=2 sts=2 sw=2 fenc=utf-8 :vi│ 3 | ╞══════════════════════════════════════════════════════════════════════════════╡ 4 | │ Copyright © 2022, Gautham Venkatasubramanian │ 5 | │ │ 6 | │ Permission to use, copy, modify, and/or distribute this software for │ 7 | │ any purpose with or without fee is hereby granted, provided that the │ 8 | │ above copyright notice and this permission notice appear in all copies. │ 9 | │ │ 10 | │ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ 11 | │ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ 12 | │ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ 13 | │ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ 14 | │ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ 15 | │ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ 16 | │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ 17 | │ PERFORMANCE OF THIS SOFTWARE. │ 18 | ╚─────────────────────────────────────────────────────────────────────────────*/ 19 | #include 20 | 21 | int plugin_is_GPL_compatible; 22 | 23 | static struct plugin_name_args portcosmo_info = {.base_name = PORTCOSMO, 24 | .version = PORTCOSMO_VERSION, 25 | .help = PORTCOSMO_HELP}; 26 | 27 | /* FREE THE PARTS OF THIS AT PLUGIN_FINISH */ 28 | SubContext plugin_context; 29 | 30 | void handle_finish(void *gcc_data, void *user_data) { 31 | SubContext *ctx = (SubContext *)user_data; 32 | if (ctx != &plugin_context) { 33 | fatal_error(MAX_LOCATION_T, "unable to clear plugin data!"); 34 | } else { 35 | cleanup_context(ctx); 36 | } 37 | } 38 | 39 | int plugin_init(struct plugin_name_args *plugin_info, 40 | struct plugin_gcc_version *version) { 41 | if (!plugin_default_version_check(version, &gcc_version)) { 42 | DEBUGF("GCC version incompatible!\n"); 43 | return 1; 44 | } 45 | construct_context(&plugin_context); 46 | 47 | DEBUGF("Loading plugin %s on GCC %s...\n", plugin_info->base_name, 48 | version->basever); 49 | register_callback(plugin_info->base_name, PLUGIN_INFO, NULL, &portcosmo_info); 50 | register_callback(plugin_info->base_name, PLUGIN_START_UNIT, handle_start_tu, 51 | &plugin_context); 52 | register_callback(plugin_info->base_name, PLUGIN_PRE_GENERICIZE, 53 | handle_pre_genericize, &plugin_context); 54 | register_callback(plugin_info->base_name, PLUGIN_FINISH_DECL, handle_decl, 55 | &plugin_context); 56 | register_callback(plugin_info->base_name, PLUGIN_FINISH_UNIT, 57 | handle_finish_tu, &plugin_context); 58 | register_callback(plugin_info->base_name, PLUGIN_FINISH, handle_finish, 59 | &plugin_context); 60 | return 0; 61 | } 62 | -------------------------------------------------------------------------------- /src/portcosmo.h: -------------------------------------------------------------------------------- 1 | #ifndef PORTCOSMO_H 2 | #define PORTCOSMO_H 3 | #include 4 | /* gcc utils first */ 5 | #include 6 | #include 7 | #include 8 | 9 | #define PORTCOSMO "portcosmo" 10 | #define PORTCOSMO_VERSION "0.0.1" 11 | #define PORTCOSMO_HELP "help porting C software to Cosmopolitan Libc" 12 | 13 | void handle_start_tu(void *, void *); 14 | void handle_finish_tu(void *, void *); 15 | void handle_pre_genericize(void *, void *); 16 | 17 | #endif /* PORTCOSMO_H */ 18 | -------------------------------------------------------------------------------- /src/replace_int.cc: -------------------------------------------------------------------------------- 1 | /*- mode:c++;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ 2 | │vi: set net ft=c++ ts=2 sts=2 sw=2 fenc=utf-8 :vi│ 3 | ╞══════════════════════════════════════════════════════════════════════════════╡ 4 | │ Copyright © 2022, Gautham Venkatasubramanian │ 5 | │ │ 6 | │ Permission to use, copy, modify, and/or distribute this software for │ 7 | │ any purpose with or without fee is hereby granted, provided that the │ 8 | │ above copyright notice and this permission notice appear in all copies. │ 9 | │ │ 10 | │ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ 11 | │ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ 12 | │ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ 13 | │ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ 14 | │ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ 15 | │ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ 16 | │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ 17 | │ PERFORMANCE OF THIS SOFTWARE. │ 18 | ╚─────────────────────────────────────────────────────────────────────────────*/ 19 | #include 20 | 21 | int arg_should_be_modded(tree arg, const subu_node *use, tree *rep_ptr) { 22 | /* if we are returning 1, rep_ptr has been set. 23 | * if we are returning 0, rep_ptr is unchanged. 24 | * use is not affected! */ 25 | if (TREE_CODE(arg) == INTEGER_CST) { 26 | tree vx = DECL_INITIAL(get_ifsw_identifier(use->name)); 27 | if (tree_int_cst_equal(arg, vx)) { 28 | /* if this is an integer constant, AND its 29 | * value is equal to the macro we substituted, 30 | * then we replace the correct variable here */ 31 | *rep_ptr = 32 | build1(NOP_EXPR, integer_type_node, VAR_NAME_AS_TREE(use->name)); 33 | INFORM(use->loc, "replaced an integer here with %s\n", use->name); 34 | return 1; 35 | } 36 | /* here you might want to handle some 37 | * minimal constant folding algebra, 38 | * like -VAR or ~VAR */ 39 | if (tree_fits_poly_int64_p(vx) && tree_fits_poly_int64_p(arg)) { 40 | auto v1 = tree_to_poly_int64(vx); 41 | auto v2 = tree_to_poly_int64(arg); 42 | 43 | /* handle the -VAR case */ 44 | if (known_eq(v1, -v2)) { 45 | INFORM(use->loc, "replaced an integer here with -%s\n", use->name); 46 | *rep_ptr = 47 | build1(NEGATE_EXPR, integer_type_node, VAR_NAME_AS_TREE(use->name)); 48 | return 1; 49 | } 50 | 51 | /* handle the ~VAR case */ 52 | if (known_eq(v1, ~v2)) { 53 | INFORM(use->loc, "replaced an integer here with ~%s\n", use->name); 54 | *rep_ptr = build1(BIT_NOT_EXPR, integer_type_node, 55 | VAR_NAME_AS_TREE(use->name)); 56 | return 1; 57 | } 58 | } 59 | return 0; 60 | } else if (TREE_CODE(arg) == NOP_EXPR && 61 | TREE_OPERAND(arg, 0) == get_ifsw_identifier(use->name)) { 62 | /* we have a situation where the compiler did not fold 63 | * the constant, ie the AST has something like 64 | * 65 | * foo(x, __tmpcosmo_VAR); 66 | * 67 | * instead of 68 | * 69 | * foo(x, ); 70 | * 71 | * in that case, this check should activate, as 72 | * get_ifsw_identifier will return __tmpcosmo_VAR, 73 | * and we return the modification necessary, ie: 74 | * 75 | * foo(x, VAR); 76 | * */ 77 | *rep_ptr = build1(NOP_EXPR, integer_type_node, VAR_NAME_AS_TREE(use->name)); 78 | return 1; 79 | } 80 | 81 | return 0; 82 | } 83 | -------------------------------------------------------------------------------- /src/subcontext.cc: -------------------------------------------------------------------------------- 1 | /*- mode:c++;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ 2 | │vi: set net ft=c++ ts=2 sts=2 sw=2 fenc=utf-8 :vi│ 3 | ╞══════════════════════════════════════════════════════════════════════════════╡ 4 | │ Copyright © 2022, Gautham Venkatasubramanian │ 5 | │ │ 6 | │ Permission to use, copy, modify, and/or distribute this software for │ 7 | │ any purpose with or without fee is hereby granted, provided that the │ 8 | │ above copyright notice and this permission notice appear in all copies. │ 9 | │ │ 10 | │ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ 11 | │ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ 12 | │ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ 13 | │ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ 14 | │ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ 15 | │ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ 16 | │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ 17 | │ PERFORMANCE OF THIS SOFTWARE. │ 18 | ╚─────────────────────────────────────────────────────────────────────────────*/ 19 | #include 20 | 21 | subu_node *build_subu(const location_t loc, const char *name, 22 | unsigned int namelen, SubstType tp) { 23 | /* xmalloc because malloc is poisoned by gcc-plugin's system.h */ 24 | subu_node *res = (subu_node *)xmalloc(sizeof(subu_node)); 25 | res->next = NULL; 26 | res->loc = loc; 27 | res->name = xstrndup(name, namelen); 28 | res->tp = tp; 29 | DEBUGF("allocated subu_node at %p\n", res); 30 | return res; 31 | }; 32 | 33 | void delete_subu(subu_node *node) { 34 | DEBUGF("freeing subu_node at %p, %u,%u\n", node, LOCATION_LINE(node->loc), 35 | LOCATION_COLUMN(node->loc)); 36 | node->loc = 0x0; 37 | free(node->name); 38 | node->next = NULL; 39 | node->tp = UNKNOWN; 40 | free(node); 41 | } 42 | 43 | subu_list *init_subu_list() { 44 | subu_list *res = (subu_list *)xmalloc(sizeof(subu_list)); 45 | res->head = NULL; 46 | res->count = 0; 47 | res->start = 0; 48 | res->end = 0; 49 | DEBUGF("allocated subu_list at %p\n", res); 50 | return res; 51 | } 52 | 53 | static void recount_subu_list(subu_list *list) { 54 | int i = 0; 55 | location_t s = MAX_LOCATION_T; 56 | location_t e = 0; 57 | subu_node *it; 58 | for (it = list->head; it != NULL; it = it->next) { 59 | i += 1; 60 | /* is it possible to compare for s and e? */ 61 | if (s == MAX_LOCATION_T || LOCATION_BEFORE2(it->loc, s)) s = it->loc; 62 | if (LOCATION_AFTER2(it->loc, e)) e = it->loc; 63 | } 64 | if (LOCATION_AFTER2(s, e)) { 65 | s = e; 66 | } 67 | list->start = s; 68 | list->end = e; 69 | list->count = i; 70 | DEBUGF("list with %d subus, start = %u,%u end = %u,%u\n", list->count, 71 | LOCATION_LINE(list->start), LOCATION_COLUMN(list->start), 72 | LOCATION_LINE(list->end), LOCATION_COLUMN(list->end)); 73 | } 74 | 75 | void add_subu_elem(subu_list *list, subu_node *node) { 76 | subu_node *tmp; 77 | if (list->head == NULL) { 78 | list->head = node; 79 | } else { 80 | for (tmp = list->head; tmp->next != NULL; tmp = tmp->next) 81 | ; 82 | tmp->next = node; 83 | node->next = NULL; 84 | } 85 | recount_subu_list(list); 86 | } 87 | 88 | void pop_subu_list(subu_list *list) { 89 | if (list->head != NULL) { 90 | subu_node *tmp = list->head; 91 | list->head = list->head->next; 92 | delete_subu(tmp); 93 | } 94 | recount_subu_list(list); 95 | } 96 | 97 | int valid_subu_bounds(subu_list *list, location_t start, location_t end) { 98 | /* return 1 if the bounds of list and provided bounds overlap */ 99 | if (LOCATION_BEFORE(list->start, end) && LOCATION_AFTER(list->start, start)) 100 | return 1; 101 | if (LOCATION_BEFORE(start, list->end) && LOCATION_AFTER(start, list->start)) 102 | return 1; 103 | return 0; 104 | } 105 | 106 | int check_loc_in_bound(subu_list *list, location_t loc) { 107 | /* return 1 if loc is within the bounds */ 108 | if (LOCATION_BEFORE(list->start, loc) && LOCATION_AFTER(list->end, loc)) { 109 | return 1; 110 | } else { 111 | return 0; 112 | } 113 | } 114 | 115 | int get_subu_elem(subu_list *list, location_t loc, subu_node **node) { 116 | /* *node is overwritten on returning 1 ie success */ 117 | subu_node *it = list->head; 118 | for (; it != NULL; it = it->next) { 119 | if (LOCATION_APPROX(it->loc, loc)) { 120 | *node = it; 121 | return 1; 122 | } 123 | } 124 | return 0; 125 | } 126 | 127 | int get_subu_elem2(subu_list *list, source_range rng, subu_node **node) { 128 | /* *node is overwritten on returning 1 ie success */ 129 | /* returns the first node found within rng's bounds */ 130 | subu_node *it = list->head; 131 | for (; it != NULL; it = it->next) { 132 | if (LOCATION_BEFORE(rng.m_start, it->loc) && 133 | LOCATION_AFTER(rng.m_finish, it->loc)) { 134 | *node = it; 135 | return 1; 136 | } 137 | } 138 | return 0; 139 | } 140 | 141 | void remove_subu_elem(subu_list *list, subu_node *node) { 142 | subu_node *cur, *prev; 143 | if (list->head != NULL) { 144 | if (list->head == node) { 145 | cur = list->head; 146 | list->head = list->head->next; 147 | delete_subu(cur); 148 | } else { 149 | prev = list->head; 150 | cur = list->head->next; 151 | for (; cur != NULL; prev = cur, cur = cur->next) { 152 | if (cur == node) { 153 | prev->next = cur->next; 154 | delete_subu(cur); 155 | break; 156 | } 157 | } 158 | } 159 | recount_subu_list(list); 160 | } 161 | } 162 | 163 | void clear_subu_list(subu_list *list) { 164 | subu_node *it, *tmp; 165 | for (it = list->head; it != NULL;) { 166 | tmp = it; 167 | it = it->next; 168 | delete_subu(tmp); 169 | } 170 | list->head = NULL; 171 | list->count = 0; 172 | list->start = 0; 173 | list->end = 0; 174 | } 175 | 176 | void delete_subu_list(subu_list *list) { 177 | clear_subu_list(list); 178 | free(list); 179 | DEBUGF("freeing subu_list at %p\n", list); 180 | } 181 | 182 | int check_empty_subu_list(subu_list *list, location_t start) { 183 | /* we should have modded all locations before start, and so 184 | * list should not contain any entries which have a location 185 | * before start */ 186 | int errcount = 0; 187 | for (auto it = list->head; it; it = it->next) { 188 | if (start == MAX_LOCATION_T || LOCATION_BEFORE2(it->loc, start)) { 189 | error_at(it->loc, "unable to substitute constant\n"); 190 | errcount += 1; 191 | } 192 | } 193 | if (errcount != 0) { 194 | /* DON'T DELETE! */ 195 | clear_subu_list(list); 196 | } 197 | return errcount == 0; 198 | } 199 | 200 | void construct_context(SubContext *ctx) { 201 | ctx->active = 0; 202 | ctx->mods = init_subu_list(); 203 | ctx->prev = NULL; 204 | ctx->switchcount = 0; 205 | ctx->initcount = 0; 206 | ctx->subcount = 0; 207 | } 208 | 209 | void add_context_subu(SubContext *ctx, const location_t loc, const char *defn, 210 | unsigned int at, SubstType st) { 211 | if (ctx->mods == NULL) return; 212 | add_subu_elem(ctx->mods, build_subu(loc, defn, at, st)); 213 | } 214 | 215 | void check_context_clear(SubContext *ctx, location_t start) { 216 | if (ctx->mods) { 217 | ctx->active = check_empty_subu_list(ctx->mods, start); 218 | } 219 | } 220 | 221 | void cleanup_context(SubContext *ctx) { 222 | check_context_clear(ctx, MAX_LOCATION_T); 223 | if (ctx->mods) { 224 | delete_subu_list(ctx->mods); 225 | ctx->mods = NULL; 226 | } 227 | ctx->prev = NULL; 228 | if (ctx->switchcount > 0) { 229 | inform(UNKNOWN_LOCATION, "rewrote %u switch statements", ctx->switchcount); 230 | } 231 | ctx->switchcount = 0; 232 | if (ctx->initcount > 0) { 233 | inform(UNKNOWN_LOCATION, "modified %u initializations", ctx->initcount); 234 | } 235 | ctx->initcount = 0; 236 | if (ctx->subcount > 0) { 237 | inform(UNKNOWN_LOCATION, "modified %u other macro uses", ctx->subcount); 238 | } 239 | ctx->subcount = 0; 240 | } 241 | -------------------------------------------------------------------------------- /src/subcontext.h: -------------------------------------------------------------------------------- 1 | #ifndef SUBCONTEXT_H 2 | #define SUBCONTEXT_H 3 | #include 4 | 5 | enum SubstType { UNKNOWN = 0, SW_CASE = 1, INITVAL = 2 }; 6 | 7 | struct _subu_node { 8 | /* a node indicating that an ifswitch substitution has occurred. 9 | * 10 | * Details include: 11 | * 12 | * - location_t of the substitution 13 | * - char* of name of the macro that was substituted (alloc'd) 14 | * - whether the substitution was inside a switch statement 15 | * - _subu_node* pointer to the next element in the list (NULL if last) 16 | * 17 | * the idea is that every time one of our modified macros is used, 18 | * we record the substitution, and then we delete this record if 19 | * we find the appropriate location_t during pre-genericize and 20 | * construct the necessary parse trees at that point. 21 | * 22 | * at the end of compilation (ie PLUGIN_FINISH), there should be 23 | * no subu_nodes allocated. 24 | */ 25 | location_t loc; 26 | SubstType tp; 27 | char *name; 28 | struct _subu_node *next; 29 | }; 30 | 31 | typedef struct _subu_node subu_node; 32 | 33 | struct _subu_list { 34 | subu_node *head; 35 | /* inclusive bounds, range containing all recorded substitutions */ 36 | location_t start, end; 37 | /* number of substitutions */ 38 | int count; 39 | }; 40 | typedef struct _subu_list subu_list; 41 | 42 | int check_loc_in_bound(subu_list *, location_t); 43 | int valid_subu_bounds(subu_list *, location_t, location_t); 44 | int get_subu_elem(subu_list *, location_t, subu_node **); 45 | int get_subu_elem2(subu_list *, source_range, subu_node **); 46 | void remove_subu_elem(subu_list *, subu_node *); 47 | 48 | /* Substitution Context */ 49 | struct SubContext { 50 | /* record all macro uses */ 51 | subu_list *mods; 52 | /* address of the previous statement we walked through, 53 | * in case we missed modding it and have to retry */ 54 | tree *prev; 55 | /* count number of switch statements rewritten */ 56 | unsigned int switchcount; 57 | /* count number of initializations rewritten */ 58 | unsigned int initcount; 59 | /* count number of other substitutions rewritten */ 60 | unsigned int subcount; 61 | /* if zero, it means we haven't started or something 62 | * went wrong somewhere */ 63 | int active; 64 | }; 65 | 66 | void add_context_subu(SubContext *, const location_t, const char *, 67 | unsigned int, SubstType); 68 | void construct_context(SubContext *); 69 | void check_context_clear(SubContext *, location_t); 70 | void cleanup_context(SubContext *); 71 | 72 | int arg_should_be_modded(tree, const subu_node *, tree *); 73 | 74 | #endif /* SUBCONTEXT_H */ 75 | -------------------------------------------------------------------------------- /src/utils.cc: -------------------------------------------------------------------------------- 1 | /*- mode:c++;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ 2 | │vi: set net ft=c++ ts=2 sts=2 sw=2 fenc=utf-8 :vi│ 3 | ╞══════════════════════════════════════════════════════════════════════════════╡ 4 | │ Copyright © 2022, Gautham Venkatasubramanian │ 5 | │ │ 6 | │ Permission to use, copy, modify, and/or distribute this software for │ 7 | │ any purpose with or without fee is hereby granted, provided that the │ 8 | │ above copyright notice and this permission notice appear in all copies. │ 9 | │ │ 10 | │ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ 11 | │ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ 12 | │ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ 13 | │ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ 14 | │ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ 15 | │ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ 16 | │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ 17 | │ PERFORMANCE OF THIS SOFTWARE. │ 18 | ╚─────────────────────────────────────────────────────────────────────────────*/ 19 | #include 20 | 21 | const char *get_tree_code_str(tree expr) { 22 | #define END_OF_BASE_TREE_CODES 23 | #define DEFTREECODE(a, b, c, d) \ 24 | case a: \ 25 | return b; 26 | switch (TREE_CODE(expr)) { 27 | #include "all-tree.def" 28 | default: 29 | return ""; 30 | } 31 | #undef DEFTREECODE 32 | #undef END_OF_BASE_TREE_CODES 33 | } 34 | 35 | tree get_ifsw_identifier(char *s) { 36 | char *result = (char *)xmalloc(strlen("__tmpcosmo_") + strlen(s) + 1); 37 | strcpy(result, "__tmpcosmo_"); 38 | strcat(result, s); 39 | tree t = lookup_name(get_identifier(result)); 40 | free(result); 41 | return t; 42 | } 43 | 44 | int get_value_of_const(char *name) { 45 | tree vx = get_ifsw_identifier(name); 46 | int z = tree_to_shwi(DECL_INITIAL(vx)); 47 | return z; 48 | } 49 | 50 | int check_magic_equal(tree value, char *varname) { 51 | tree vx = get_ifsw_identifier(varname); 52 | return tree_int_cst_equal(value, DECL_INITIAL(vx)); 53 | } 54 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef PORTCOSMO_UTILS_H 2 | #define PORTCOSMO_UTILS_H 3 | /* first stdlib headers */ 4 | #include 5 | /* now all the plugin headers */ 6 | #include 7 | /* first gcc-plugin, then the others */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | const char *get_tree_code_str(tree); 21 | int get_value_of_const(char *); 22 | tree get_ifsw_identifier(char *); 23 | int check_magic_equal(tree, char *); 24 | 25 | /* useful macros */ 26 | #define EXPR_LOC_LINE(x) LOCATION_LINE(EXPR_LOCATION((x))) 27 | #define EXPR_LOC_COL(x) LOCATION_COLUMN(EXPR_LOCATION((x))) 28 | #define LOCATION_APPROX(x, y) (LOCATION_LINE((x)) == LOCATION_LINE((y))) 29 | #define LOCATION_BEFORE(x, y) (LOCATION_LINE((x)) <= LOCATION_LINE((y))) 30 | #define LOCATION_AFTER(x, y) (LOCATION_LINE((x)) >= LOCATION_LINE((y))) 31 | 32 | #define LOCATION_BEFORE2(x, y) \ 33 | (LOCATION_LINE((x)) < LOCATION_LINE((y)) || \ 34 | (LOCATION_LINE((x)) == LOCATION_LINE((y)) && \ 35 | LOCATION_COLUMN((x)) <= LOCATION_COLUMN((y)))) 36 | #define LOCATION_AFTER2(x, y) \ 37 | (LOCATION_LINE((x)) > LOCATION_LINE((y)) || \ 38 | (LOCATION_LINE((x)) == LOCATION_LINE((y)) && \ 39 | LOCATION_COLUMN((x)) >= LOCATION_COLUMN((y)))) 40 | 41 | #define VAR_NAME_AS_TREE(fname) lookup_name(get_identifier((fname))) 42 | #define IDENTIFIER_NAME(z) IDENTIFIER_POINTER(DECL_NAME((z))) 43 | #define BUILD_STRING_AS_TREE(str) build_string_literal(strlen((str)) + 1, (str)) 44 | 45 | #ifndef NDEBUG 46 | #define DEBUGF(...) fprintf(stderr, " " __VA_ARGS__) 47 | #define INFORM(...) inform(__VA_ARGS__) 48 | #else 49 | #define DEBUGF(...) 50 | #define INFORM(...) 51 | #endif 52 | 53 | #define STRING_BUFFER_SIZE 192 54 | 55 | #endif /* PORTCOSMO_UTILS_H */ 56 | -------------------------------------------------------------------------------- /tmpconst.h: -------------------------------------------------------------------------------- 1 | #ifdef ACTUALLY 2 | #ifndef ACTUALLY_MODS 3 | #define ACTUALLY_MODS 4 | static const int __tmpcosmo_AF_ALG = -15823936; 5 | static const int __tmpcosmo_AF_APPLETALK = -15823820; 6 | static const int __tmpcosmo_AF_ASH = -15823924; 7 | static const int __tmpcosmo_AF_ATMPVC = -15824070; 8 | static const int __tmpcosmo_AF_ATMSVC = -15824056; 9 | static const int __tmpcosmo_AF_AX25 = -15824014; 10 | static const int __tmpcosmo_AF_BLUETOOTH = -15823992; 11 | static const int __tmpcosmo_AF_BRIDGE = -15823812; 12 | static const int __tmpcosmo_AF_CAIF = -15823850; 13 | static const int __tmpcosmo_AF_CAN = -15823868; 14 | static const int __tmpcosmo_AF_ECONET = -15823852; 15 | static const int __tmpcosmo_AF_FILE = -15824118; 16 | static const int __tmpcosmo_AF_IB = -15823966; 17 | static const int __tmpcosmo_AF_IEEE802154 = -15823906; 18 | static const int __tmpcosmo_AF_IPX = -15824002; 19 | static const int __tmpcosmo_AF_IRDA = -15823860; 20 | static const int __tmpcosmo_AF_ISDN = -15823978; 21 | static const int __tmpcosmo_AF_IUCV = -15824106; 22 | static const int __tmpcosmo_AF_KCM = -15824024; 23 | static const int __tmpcosmo_AF_KEY = -15823948; 24 | static const int __tmpcosmo_AF_LINK = -15823878; 25 | static const int __tmpcosmo_AF_LLC = -15823824; 26 | static const int __tmpcosmo_AF_LOCAL = -15823928; 27 | static const int __tmpcosmo_AF_MAX = -15824082; 28 | static const int __tmpcosmo_AF_MPLS = -15824026; 29 | static const int __tmpcosmo_AF_NETBEUI = -15824124; 30 | static const int __tmpcosmo_AF_NETLINK = -15824004; 31 | static const int __tmpcosmo_AF_NETROM = -15823886; 32 | static const int __tmpcosmo_AF_NFC = -15824142; 33 | static const int __tmpcosmo_AF_PACKET = -15824028; 34 | static const int __tmpcosmo_AF_PHONET = -15823830; 35 | static const int __tmpcosmo_AF_PPPOX = -15823876; 36 | static const int __tmpcosmo_AF_ROSE = -15824016; 37 | static const int __tmpcosmo_AF_ROUTE = -15824100; 38 | static const int __tmpcosmo_AF_RXRPC = -15823926; 39 | static const int __tmpcosmo_AF_SECURITY = -15824136; 40 | static const int __tmpcosmo_AF_SNA = -15823950; 41 | static const int __tmpcosmo_AF_TIPC = -15824034; 42 | static const int __tmpcosmo_AF_VSOCK = -15824146; 43 | static const int __tmpcosmo_AF_WANPIPE = -15823960; 44 | static const int __tmpcosmo_AF_X25 = -15823864; 45 | static const int __tmpcosmo_E2BIG = -15823698; 46 | static const int __tmpcosmo_EACCES = -15823580; 47 | static const int __tmpcosmo_EADDRINUSE = -15823756; 48 | static const int __tmpcosmo_EADDRNOTAVAIL = -15823592; 49 | static const int __tmpcosmo_EADV = -15823574; 50 | static const int __tmpcosmo_EAFNOSUPPORT = -15823748; 51 | static const int __tmpcosmo_EAGAIN = -15823506; 52 | static const int __tmpcosmo_EALREADY = -15823530; 53 | static const int __tmpcosmo_EAUTH = -15823702; 54 | static const int __tmpcosmo_EBADARCH = -15823738; 55 | static const int __tmpcosmo_EBADE = -15823740; 56 | static const int __tmpcosmo_EBADEXEC = -15823684; 57 | static const int __tmpcosmo_EBADF = -15823744; 58 | static const int __tmpcosmo_EBADFD = -15823554; 59 | static const int __tmpcosmo_EBADMACHO = -15823618; 60 | static const int __tmpcosmo_EBADMSG = -15823650; 61 | static const int __tmpcosmo_EBADR = -15823570; 62 | static const int __tmpcosmo_EBADRPC = -15823626; 63 | static const int __tmpcosmo_EBADRQC = -15823688; 64 | static const int __tmpcosmo_EBADSLT = -15823788; 65 | static const int __tmpcosmo_EBUSY = -15823550; 66 | static const int __tmpcosmo_ECANCELED = -15823676; 67 | static const int __tmpcosmo_ECHILD = -15823662; 68 | static const int __tmpcosmo_ECHRNG = -15823722; 69 | static const int __tmpcosmo_ECOMM = -15823634; 70 | static const int __tmpcosmo_ECONNABORTED = -15823616; 71 | static const int __tmpcosmo_ECONNREFUSED = -15823556; 72 | static const int __tmpcosmo_ECONNRESET = -15823548; 73 | static const int __tmpcosmo_EDEADLK = -15823718; 74 | static const int __tmpcosmo_EDESTADDRREQ = -15823658; 75 | static const int __tmpcosmo_EDEVERR = -15823518; 76 | static const int __tmpcosmo_EDOM = -15823798; 77 | static const int __tmpcosmo_EDOTDOT = -15823726; 78 | static const int __tmpcosmo_EDQUOT = -15823620; 79 | static const int __tmpcosmo_EEXIST = -15823594; 80 | static const int __tmpcosmo_EFAULT = -15823686; 81 | static const int __tmpcosmo_EFBIG = -15823768; 82 | static const int __tmpcosmo_EFTYPE = -15823568; 83 | static const int __tmpcosmo_EHOSTDOWN = -15823596; 84 | static const int __tmpcosmo_EHOSTUNREACH = -15823742; 85 | static const int __tmpcosmo_EHWPOISON = -15823680; 86 | static const int __tmpcosmo_EIDRM = -15823644; 87 | static const int __tmpcosmo_EILSEQ = -15823540; 88 | static const int __tmpcosmo_EINPROGRESS = -15823720; 89 | static const int __tmpcosmo_EINTR = -15823710; 90 | static const int __tmpcosmo_EINVAL = -15823624; 91 | static const int __tmpcosmo_EIO = -15823544; 92 | static const int __tmpcosmo_EISCONN = -15823704; 93 | static const int __tmpcosmo_EISDIR = -15823758; 94 | static const int __tmpcosmo_EISNAM = -15823682; 95 | static const int __tmpcosmo_EKEYEXPIRED = -15823520; 96 | static const int __tmpcosmo_EKEYREJECTED = -15823712; 97 | static const int __tmpcosmo_EKEYREVOKED = -15823780; 98 | static const int __tmpcosmo_EL2HLT = -15823510; 99 | static const int __tmpcosmo_EL2NSYNC = -15823670; 100 | static const int __tmpcosmo_EL3HLT = -15823792; 101 | static const int __tmpcosmo_EL3RST = -15823654; 102 | static const int __tmpcosmo_ELIBACC = -15823708; 103 | static const int __tmpcosmo_ELIBBAD = -15823564; 104 | static const int __tmpcosmo_ELIBEXEC = -15823696; 105 | static const int __tmpcosmo_ELIBMAX = -15823724; 106 | static const int __tmpcosmo_ELIBSCN = -15823786; 107 | static const int __tmpcosmo_ELNRNG = -15823732; 108 | static const int __tmpcosmo_ELOOP = -15823672; 109 | static const int __tmpcosmo_EMEDIUMTYPE = -15823508; 110 | static const int __tmpcosmo_EMFILE = -15823762; 111 | static const int __tmpcosmo_EMLINK = -15823694; 112 | static const int __tmpcosmo_EMSGSIZE = -15823536; 113 | static const int __tmpcosmo_EMULTIHOP = -15823750; 114 | static const int __tmpcosmo_ENAMETOOLONG = -15823600; 115 | static const int __tmpcosmo_ENAVAIL = -15823656; 116 | static const int __tmpcosmo_ENEEDAUTH = -15823766; 117 | static const int __tmpcosmo_ENETDOWN = -15823730; 118 | static const int __tmpcosmo_ENETRESET = -15823604; 119 | static const int __tmpcosmo_ENETUNREACH = -15823524; 120 | static const int __tmpcosmo_ENFILE = -15823700; 121 | static const int __tmpcosmo_ENOANO = -15823734; 122 | static const int __tmpcosmo_ENOATTR = -15823606; 123 | static const int __tmpcosmo_ENOBUFS = -15823628; 124 | static const int __tmpcosmo_ENOCSI = -15823760; 125 | static const int __tmpcosmo_ENODATA = -15823516; 126 | static const int __tmpcosmo_ENODEV = -15823774; 127 | static const int __tmpcosmo_ENOENT = -15823590; 128 | static const int __tmpcosmo_ENOEXEC = -15823512; 129 | static const int __tmpcosmo_ENOKEY = -15823764; 130 | static const int __tmpcosmo_ENOLCK = -15823782; 131 | static const int __tmpcosmo_ENOLINK = -15823538; 132 | static const int __tmpcosmo_ENOMEDIUM = -15823598; 133 | static const int __tmpcosmo_ENOMEM = -15823514; 134 | static const int __tmpcosmo_ENOMSG = -15823796; 135 | static const int __tmpcosmo_ENONET = -15823642; 136 | static const int __tmpcosmo_ENOPKG = -15823664; 137 | static const int __tmpcosmo_ENOPOLICY = -15823716; 138 | static const int __tmpcosmo_ENOPROTOOPT = -15823608; 139 | static const int __tmpcosmo_ENOSPC = -15823646; 140 | static const int __tmpcosmo_ENOSR = -15823558; 141 | static const int __tmpcosmo_ENOSTR = -15823706; 142 | static const int __tmpcosmo_ENOSYS = -15823636; 143 | static const int __tmpcosmo_ENOTBLK = -15823640; 144 | static const int __tmpcosmo_ENOTCONN = -15823778; 145 | static const int __tmpcosmo_ENOTDIR = -15823648; 146 | static const int __tmpcosmo_ENOTEMPTY = -15823552; 147 | static const int __tmpcosmo_ENOTNAM = -15823532; 148 | static const int __tmpcosmo_ENOTRECOVERABLE = -15823746; 149 | static const int __tmpcosmo_ENOTSOCK = -15823582; 150 | static const int __tmpcosmo_ENOTSUP = -15823602; 151 | static const int __tmpcosmo_ENOTTY = -15823528; 152 | static const int __tmpcosmo_ENOTUNIQ = -15823790; 153 | static const int __tmpcosmo_ENXIO = -15823622; 154 | static const int __tmpcosmo_EOPNOTSUPP = -15823588; 155 | static const int __tmpcosmo_EOVERFLOW = -15823736; 156 | static const int __tmpcosmo_EOWNERDEAD = -15823562; 157 | static const int __tmpcosmo_EPERM = -15823754; 158 | static const int __tmpcosmo_EPFNOSUPPORT = -15823690; 159 | static const int __tmpcosmo_EPIPE = -15823534; 160 | static const int __tmpcosmo_EPROCLIM = -15823610; 161 | static const int __tmpcosmo_EPROCUNAVAIL = -15823546; 162 | static const int __tmpcosmo_EPROGMISMATCH = -15823572; 163 | static const int __tmpcosmo_EPROGUNAVAIL = -15823526; 164 | static const int __tmpcosmo_EPROTO = -15823678; 165 | static const int __tmpcosmo_EPROTONOSUPPORT = -15823576; 166 | static const int __tmpcosmo_EPROTOTYPE = -15823614; 167 | static const int __tmpcosmo_EPWROFF = -15823692; 168 | static const int __tmpcosmo_ERANGE = -15823772; 169 | static const int __tmpcosmo_EREMCHG = -15823666; 170 | static const int __tmpcosmo_EREMOTE = -15823560; 171 | static const int __tmpcosmo_EREMOTEIO = -15823794; 172 | static const int __tmpcosmo_ERESTART = -15823728; 173 | static const int __tmpcosmo_ERFKILL = -15823612; 174 | static const int __tmpcosmo_EROFS = -15823566; 175 | static const int __tmpcosmo_ERPCMISMATCH = -15823542; 176 | static const int __tmpcosmo_ESHLIBVERS = -15823584; 177 | static const int __tmpcosmo_ESHUTDOWN = -15823660; 178 | static const int __tmpcosmo_ESOCKTNOSUPPORT = -15823776; 179 | static const int __tmpcosmo_ESPIPE = -15823652; 180 | static const int __tmpcosmo_ESRCH = -15823674; 181 | static const int __tmpcosmo_ESRMNT = -15823714; 182 | static const int __tmpcosmo_ESTALE = -15823632; 183 | static const int __tmpcosmo_ESTRPIPE = -15823770; 184 | static const int __tmpcosmo_ETIME = -15823630; 185 | static const int __tmpcosmo_ETIMEDOUT = -15823522; 186 | static const int __tmpcosmo_ETOOMANYREFS = -15823586; 187 | static const int __tmpcosmo_ETXTBSY = -15823638; 188 | static const int __tmpcosmo_EUCLEAN = -15823578; 189 | static const int __tmpcosmo_EUNATCH = -15823504; 190 | static const int __tmpcosmo_EUSERS = -15823668; 191 | static const int __tmpcosmo_EXDEV = -15823752; 192 | static const int __tmpcosmo_EXFULL = -15823784; 193 | static const int __tmpcosmo_F_DUPFD_CLOEXEC = -15823938; 194 | static const int __tmpcosmo_F_GETLEASE = -15823862; 195 | static const int __tmpcosmo_F_GETLK = -15823916; 196 | static const int __tmpcosmo_F_GETLK64 = -15823846; 197 | static const int __tmpcosmo_F_GETOWN = -15824116; 198 | static const int __tmpcosmo_F_GETPATH = -15824128; 199 | static const int __tmpcosmo_F_GETPIPE_SZ = -15824006; 200 | static const int __tmpcosmo_F_GETSIG = -15824112; 201 | static const int __tmpcosmo_F_MAXFD = -15823896; 202 | static const int __tmpcosmo_F_NOCACHE = -15824048; 203 | static const int __tmpcosmo_F_NOTIFY = -15823898; 204 | static const int __tmpcosmo_F_RDLCK = -15823826; 205 | static const int __tmpcosmo_F_SETLEASE = -15823884; 206 | static const int __tmpcosmo_F_SETLK = -15824088; 207 | static const int __tmpcosmo_F_SETLK64 = -15824154; 208 | static const int __tmpcosmo_F_SETLKW = -15824096; 209 | static const int __tmpcosmo_F_SETLKW64 = -15824104; 210 | static const int __tmpcosmo_F_SETOWN = -15823874; 211 | static const int __tmpcosmo_F_SETPIPE_SZ = -15823958; 212 | static const int __tmpcosmo_F_SETSIG = -15823832; 213 | static const int __tmpcosmo_F_UNLCK = -15824148; 214 | static const int __tmpcosmo_F_WRLCK = -15824058; 215 | static const int __tmpcosmo_IFF_ALLMULTI = -15824140; 216 | static const int __tmpcosmo_IFF_AUTOMEDIA = -15823962; 217 | static const int __tmpcosmo_IFF_DYNAMIC = -15823848; 218 | static const int __tmpcosmo_IFF_MASTER = -15823900; 219 | static const int __tmpcosmo_IFF_MULTICAST = -15824000; 220 | static const int __tmpcosmo_IFF_NOARP = -15823802; 221 | static const int __tmpcosmo_IFF_NOTRAILERS = -15824130; 222 | static const int __tmpcosmo_IFF_POINTOPOINT = -15824138; 223 | static const int __tmpcosmo_IFF_PORTSEL = -15824150; 224 | static const int __tmpcosmo_IFF_PROMISC = -15824010; 225 | static const int __tmpcosmo_IFF_RUNNING = -15824080; 226 | static const int __tmpcosmo_IFF_SLAVE = -15824022; 227 | static const int __tmpcosmo_LOCAL_PEERCRED = -15823986; 228 | static const int __tmpcosmo_SIGBUS = -15824132; 229 | static const int __tmpcosmo_SIGCHLD = -15824036; 230 | static const int __tmpcosmo_SIGCONT = -15823836; 231 | static const int __tmpcosmo_SIGEMT = -15823972; 232 | static const int __tmpcosmo_SIGINFO = -15824086; 233 | static const int __tmpcosmo_SIGIO = -15823912; 234 | static const int __tmpcosmo_SIGPOLL = -15823854; 235 | static const int __tmpcosmo_SIGPWR = -15824114; 236 | static const int __tmpcosmo_SIGRTMAX = -15824040; 237 | static const int __tmpcosmo_SIGRTMIN = -15824134; 238 | static const int __tmpcosmo_SIGSTKFLT = -15823934; 239 | static const int __tmpcosmo_SIGSTOP = -15824158; 240 | static const int __tmpcosmo_SIGSYS = -15823922; 241 | static const int __tmpcosmo_SIGTHR = -15823902; 242 | static const int __tmpcosmo_SIGTSTP = -15823988; 243 | static const int __tmpcosmo_SIGUNUSED = -15823970; 244 | static const int __tmpcosmo_SIGURG = -15823952; 245 | static const int __tmpcosmo_SIGUSR1 = -15824018; 246 | static const int __tmpcosmo_SIGUSR2 = -15823998; 247 | static const int __tmpcosmo_SIG_BLOCK = -15823800; 248 | static const int __tmpcosmo_SIG_SETMASK = -15824090; 249 | static const int __tmpcosmo_SIG_UNBLOCK = -15824078; 250 | static const int __tmpcosmo_SOL_AAL = -15823976; 251 | static const int __tmpcosmo_SOL_ALG = -15823956; 252 | static const int __tmpcosmo_SOL_ATM = -15823914; 253 | static const int __tmpcosmo_SOL_BLUETOOTH = -15824062; 254 | static const int __tmpcosmo_SOL_CAIF = -15823904; 255 | static const int __tmpcosmo_SOL_DCCP = -15823814; 256 | static const int __tmpcosmo_SOL_DECNET = -15823842; 257 | static const int __tmpcosmo_SOL_ICMPV6 = -15823908; 258 | static const int __tmpcosmo_SOL_IPV6 = -15823808; 259 | static const int __tmpcosmo_SOL_IRDA = -15823880; 260 | static const int __tmpcosmo_SOL_IUCV = -15824156; 261 | static const int __tmpcosmo_SOL_KCM = -15824092; 262 | static const int __tmpcosmo_SOL_LLC = -15823930; 263 | static const int __tmpcosmo_SOL_NETBEUI = -15823894; 264 | static const int __tmpcosmo_SOL_NETLINK = -15824012; 265 | static const int __tmpcosmo_SOL_NFC = -15823942; 266 | static const int __tmpcosmo_SOL_PACKET = -15823806; 267 | static const int __tmpcosmo_SOL_PNPIPE = -15823968; 268 | static const int __tmpcosmo_SOL_PPPOL2TP = -15823816; 269 | static const int __tmpcosmo_SOL_RAW = -15824044; 270 | static const int __tmpcosmo_SOL_RDS = -15824020; 271 | static const int __tmpcosmo_SOL_RXRPC = -15823984; 272 | static const int __tmpcosmo_SOL_SOCKET = -15824050; 273 | static const int __tmpcosmo_SOL_TIPC = -15823940; 274 | static const int __tmpcosmo_SOL_X25 = -15823856; 275 | static const int __tmpcosmo_SO_ACCEPTCONN = -15823872; 276 | static const int __tmpcosmo_SO_ATTACH_BPF = -15824072; 277 | static const int __tmpcosmo_SO_ATTACH_FILTER = -15824094; 278 | static const int __tmpcosmo_SO_ATTACH_REUSEPORT_CBPF = -15823964; 279 | static const int __tmpcosmo_SO_ATTACH_REUSEPORT_EBPF = -15824060; 280 | static const int __tmpcosmo_SO_BINDTODEVICE = -15823990; 281 | static const int __tmpcosmo_SO_BPF_EXTENSIONS = -15824030; 282 | static const int __tmpcosmo_SO_BROADCAST = -15823882; 283 | static const int __tmpcosmo_SO_BSDCOMPAT = -15824038; 284 | static const int __tmpcosmo_SO_BUSY_POLL = -15823944; 285 | static const int __tmpcosmo_SO_CNX_ADVICE = -15823828; 286 | static const int __tmpcosmo_SO_DETACH_BPF = -15824068; 287 | static const int __tmpcosmo_SO_DETACH_FILTER = -15824032; 288 | static const int __tmpcosmo_SO_DOMAIN = -15823980; 289 | static const int __tmpcosmo_SO_DONTROUTE = -15823918; 290 | static const int __tmpcosmo_SO_ERROR = -15823892; 291 | static const int __tmpcosmo_SO_EXCLUSIVEADDRUSE = -15823858; 292 | static const int __tmpcosmo_SO_GET_FILTER = -15823834; 293 | static const int __tmpcosmo_SO_INCOMING_CPU = -15824074; 294 | static const int __tmpcosmo_SO_KEEPALIVE = -15823890; 295 | static const int __tmpcosmo_SO_LINGER = -15824084; 296 | static const int __tmpcosmo_SO_LOCK_FILTER = -15823804; 297 | static const int __tmpcosmo_SO_MARK = -15824008; 298 | static const int __tmpcosmo_SO_MAX_PACING_RATE = -15824120; 299 | static const int __tmpcosmo_SO_NOFCS = -15823818; 300 | static const int __tmpcosmo_SO_NO_CHECK = -15824152; 301 | static const int __tmpcosmo_SO_OOBINLINE = -15823838; 302 | static const int __tmpcosmo_SO_PASSCRED = -15823888; 303 | static const int __tmpcosmo_SO_PASSSEC = -15823866; 304 | static const int __tmpcosmo_SO_PEEK_OFF = -15823870; 305 | static const int __tmpcosmo_SO_PEERCRED = -15823954; 306 | static const int __tmpcosmo_SO_PEERNAME = -15824042; 307 | static const int __tmpcosmo_SO_PEERSEC = -15823844; 308 | static const int __tmpcosmo_SO_PRIORITY = -15824122; 309 | static const int __tmpcosmo_SO_PROTOCOL = -15823982; 310 | static const int __tmpcosmo_SO_RCVBUF = -15823974; 311 | static const int __tmpcosmo_SO_RCVBUFFORCE = -15823994; 312 | static const int __tmpcosmo_SO_RCVLOWAT = -15824076; 313 | static const int __tmpcosmo_SO_RCVTIMEO = -15824046; 314 | static const int __tmpcosmo_SO_REUSEADDR = -15823810; 315 | static const int __tmpcosmo_SO_REUSEPORT = -15823822; 316 | static const int __tmpcosmo_SO_RXQ_OVFL = -15824066; 317 | static const int __tmpcosmo_SO_SECURITY_AUTHENTICATION = -15824098; 318 | static const int __tmpcosmo_SO_SECURITY_ENCRYPTION_NETWORK = -15824126; 319 | static const int __tmpcosmo_SO_SELECT_ERR_QUEUE = -15824052; 320 | static const int __tmpcosmo_SO_SETFIB = -15823920; 321 | static const int __tmpcosmo_SO_SNDBUF = -15824102; 322 | static const int __tmpcosmo_SO_SNDBUFFORCE = -15823840; 323 | static const int __tmpcosmo_SO_SNDLOWAT = -15823946; 324 | static const int __tmpcosmo_SO_SNDTIMEO = -15824064; 325 | static const int __tmpcosmo_SO_TIMESTAMP = -15823932; 326 | static const int __tmpcosmo_SO_TIMESTAMPING = -15824054; 327 | static const int __tmpcosmo_SO_TIMESTAMPNS = -15823910; 328 | static const int __tmpcosmo_SO_TYPE = -15824144; 329 | static const int __tmpcosmo_SO_USELOOPBACK = -15824110; 330 | static const int __tmpcosmo_SO_WIFI_STATUS = -15824108; 331 | static const unsigned int __tmpcosmo_B1000000 = 15823512; 332 | static const unsigned int __tmpcosmo_B110 = 15823518; 333 | static const unsigned int __tmpcosmo_B115200 = 15823540; 334 | static const unsigned int __tmpcosmo_B1152000 = 15823538; 335 | static const unsigned int __tmpcosmo_B1200 = 15823548; 336 | static const unsigned int __tmpcosmo_B134 = 15823510; 337 | static const unsigned int __tmpcosmo_B150 = 15823542; 338 | static const unsigned int __tmpcosmo_B1500000 = 15823508; 339 | static const unsigned int __tmpcosmo_B1800 = 15823522; 340 | static const unsigned int __tmpcosmo_B19200 = 15823546; 341 | static const unsigned int __tmpcosmo_B200 = 15823528; 342 | static const unsigned int __tmpcosmo_B2000000 = 15823524; 343 | static const unsigned int __tmpcosmo_B230400 = 15823516; 344 | static const unsigned int __tmpcosmo_B2400 = 15823526; 345 | static const unsigned int __tmpcosmo_B2500000 = 15823558; 346 | static const unsigned int __tmpcosmo_B300 = 15823534; 347 | static const unsigned int __tmpcosmo_B3000000 = 15823530; 348 | static const unsigned int __tmpcosmo_B3500000 = 15823544; 349 | static const unsigned int __tmpcosmo_B38400 = 15823514; 350 | static const unsigned int __tmpcosmo_B4000000 = 15823520; 351 | static const unsigned int __tmpcosmo_B4800 = 15823556; 352 | static const unsigned int __tmpcosmo_B50 = 15823532; 353 | static const unsigned int __tmpcosmo_B500000 = 15823550; 354 | static const unsigned int __tmpcosmo_B57600 = 15823552; 355 | static const unsigned int __tmpcosmo_B576000 = 15823506; 356 | static const unsigned int __tmpcosmo_B600 = 15823554; 357 | static const unsigned int __tmpcosmo_B75 = 15823536; 358 | static const unsigned int __tmpcosmo_B9600 = 15823504; 359 | static const unsigned short __tmpcosmo_AF_INET6 = 58236; 360 | #endif 361 | #endif 362 | --------------------------------------------------------------------------------