ACPI AML decompiler w/ CFG recovery and structured pseudocode
29

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #6 from 23384/dev

Stabilise AML parsing w refined CFG visualisation

+724 -234
+4
.gitignore
··· 4 4 *.gcda 5 5 *.gcno 6 6 *.gcov 7 + /*.dot 8 + /*.png 9 + /*.svg 10 + \#*\# 7 11 tobi 8 12 tobi_test 9 13 fz
+11
CMakeLists.txt
··· 4 4 option(TOBI_WERROR "Build with warnings as errors" ON) 5 5 option(TOBI_SAN "Build with AddressSanitizer and UndefinedBehaviorSanitizer" OFF) 6 6 option(TOBI_FZ "Build fuzz smoke target" ON) 7 + option(TOBI_LIBFUZZER "Build LLVM libFuzzer target" OFF) 7 8 8 9 set(CMAKE_C_STANDARD 11) 9 10 set(CMAKE_C_STANDARD_REQUIRED ON) ··· 66 67 target_link_options(fz PRIVATE ${SAN_FLAGS}) 67 68 endif() 68 69 endif() 70 + 71 + if(TOBI_LIBFUZZER) 72 + set(LIBFUZZER_COMPILE_FLAGS -fsanitize=fuzzer-no-link,address,undefined -fno-omit-frame-pointer) 73 + target_compile_definitions(tobilib PUBLIC TOBI_SAN_BUILD=1) 74 + target_compile_options(tobilib PRIVATE ${LIBFUZZER_COMPILE_FLAGS}) 75 + add_executable(fz_llvm fuzz/lf.c) 76 + target_link_libraries(fz_llvm PRIVATE tobilib) 77 + target_compile_options(fz_llvm PRIVATE ${TOBI_WARN} -fsanitize=fuzzer,address,undefined -fno-omit-frame-pointer) 78 + target_link_options(fz_llvm PRIVATE -fsanitize=fuzzer,address,undefined -fno-omit-frame-pointer) 79 + endif()
+45 -1
README.md
··· 1 + <p align="center"> 2 + <img src="assets/tobi.svg" alt="" width="100%"> 3 + </p> 4 + 1 5 # tobi 2 6 3 7 ACPI AML decompiler that reads raw AML blobs and DSDT/SSDT tables, skips validated ACPI headers, and builds its own IR while doing conservative CFG recovery over package bounded control operations, then emits pseudocode, opcode disassembly, JSON IR, raw table metadata/Graphviz CFG view ··· 21 25 ./build-san/fz 22 26 ``` 23 27 28 + For a bounded libFuzzer pass with the checked-in seed corpus: 29 + 30 + ```sh 31 + cmake -S . -B build-libfz -DCMAKE_C_COMPILER=clang -DTOBI_LIBFUZZER=ON -DTOBI_WERROR=ON -DTOBI_FZ=OFF 32 + cmake --build build-libfz --target fz_llvm 33 + rm -rf /tmp/tobi-fuzz-corpus 34 + mkdir -p /tmp/tobi-fuzz-corpus 35 + cp fuzz/seed/*.aml /tmp/tobi-fuzz-corpus/ 36 + ./build-libfz/fz_llvm -runs=256 /tmp/tobi-fuzz-corpus 37 + ``` 38 + 24 39 ## Usage 25 40 26 41 ```sh ··· 28 43 ``` 29 44 30 45 ```text 31 - --dis print opcode disam 46 + --dis print opcode disassembly 32 47 --json print parsed IR as json 33 48 --dot print recovered CF as Graphviz DOT 34 49 --raw print input/table metadata and AML offsets ··· 63 78 ./build/tobi --json sam/m0.aml 64 79 ``` 65 80 81 + JSON contains the aggregate parse metadata in `meta`, per-input metadata in `inputs`, source-aware diagnostics in `diagnostics`, the discovered namespace, and the deterministic IR tree. Multi-input parses keep each file/table as a named IR block and diagnostics include `source` plus `input_index` when the input name is known. 82 + 66 83 CFG view: 67 84 68 85 ```sh 69 86 ./build/tobi --dot sam/m1.aml 70 87 ``` 71 88 89 + Visualise any user supplied AML blob or ACPI table with Graphviz: 90 + 91 + ```sh 92 + ./build/tobi --dot path/to/input.aml > input.dot 93 + dot -Tsvg input.dot -o input.svg 94 + firefox input.svg 95 + ``` 96 + 97 + For one command: 98 + 99 + ```sh 100 + ./build/tobi --dot path/to/input.aml 2>/dev/null | dot -Tsvg -o input.svg 101 + ``` 102 + 72 103 Multiple AML inputs share one namespace pre pass before parsing: 73 104 74 105 ```sh ··· 99 130 raw opcode for unknown or opcode expression nodes 100 131 deterministic child array 101 132 ``` 133 + 134 + Currently covered AML surface: 135 + 136 + ```text 137 + name/scope/device/method/external/alias objects 138 + opregion, field, indexfield, bankfield, mutex, event, processor-style objects 139 + integer/string/buffer/package values and common arithmetic/logical/conversion expressions 140 + method calls resolved through a namespace pre pass, including forward and multi-file calls 141 + IfOp, ElseOp, WhileOp, ReturnOp, BreakOp, ContinueOp 142 + resource-template item summaries inside buffer objects 143 + ``` 144 + 145 + Unsupported or ambiguous opcodes are preserved as explicit `unknown` IR nodes with byte offsets and diagnostics. Pseudocode is intentionally conservative: it is an inspection format, not recompilable ASL. 102 146 103 147 ## Control flow 104 148
+6
assets/tobi.svg
··· 1 + <svg xmlns="http://www.w3.org/2000/svg" width="1600" height="1300" viewBox="0 0 1600 1300"> 2 + <rect width="1600" height="1300" fill="#ffffff"/> 3 + <image href="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIKICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPgo8IS0tIEdlbmVyYXRlZCBieSBncmFwaHZpeiB2ZXJzaW9uIDE0LjEuNSAoMCkKIC0tPgo8IS0tIFRpdGxlOiB0b2JpX2NmZyBQYWdlczogMSAtLT4KPHN2ZyB3aWR0aD0iOTZwdCIgaGVpZ2h0PSI1NThwdCIKIHZpZXdCb3g9IjAuMDAgMC4wMCA5Ni4wMCA1NTguMDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPgo8ZyBpZD0iZ3JhcGgwIiBjbGFzcz0iZ3JhcGgiIHRyYW5zZm9ybT0ic2NhbGUoMSAxKSByb3RhdGUoMCkgdHJhbnNsYXRlKDEyLjk2IDU0NC43NCkiPgo8dGl0bGU+dG9iaV9jZmc8L3RpdGxlPgo8cG9seWdvbiBmaWxsPSIjZmZmZmZmIiBzdHJva2U9Im5vbmUiIHBvaW50cz0iLTEyLjk2LDEyLjk2IC0xMi45NiwtNTQ0Ljc0IDgyLjc0LC01NDQuNzQgODIuNzQsMTIuOTYgLTEyLjk2LDEyLjk2Ii8+CjwhLS0gbjAmIzQ1OyZndDtlMiAtLT4KPGcgaWQ9ImVkZ2U1IiBjbGFzcz0iZWRnZSI+Cjx0aXRsZT5uMCYjNDU7Jmd0O2UyPC90aXRsZT4KPHBhdGggZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgZD0iTTM0Ljg5LC00OTIuNzZDMzQuODksLTQ3Ni4zMiAzNC44OSwtNDUzLjY5IDM0Ljg5LC00NDEuNDIiLz4KPC9nPgo8IS0tIG4xJiM0NTsmZ3Q7ZTAgLS0+CjxnIGlkPSJlZGdlMSIgY2xhc3M9ImVkZ2UiPgo8dGl0bGU+bjEmIzQ1OyZndDtlMDwvdGl0bGU+CjxwYXRoIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxLjEiIGQ9Ik0zNC44OSwtMzE5LjE1QzM0Ljg5LC0zMDEuNTggMzQuODksLTI3OS42MiAzNC44OSwtMjY3LjYyIi8+CjwvZz4KPCEtLSBuMiYjNDU7Jmd0O2UxIC0tPgo8ZyBpZD0iZWRnZTMiIGNsYXNzPSJlZGdlIj4KPHRpdGxlPm4yJiM0NTsmZ3Q7ZTE8L3RpdGxlPgo8cGF0aCBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMS4xIiBkPSJNMzQuODksLTE1OS4yMUMzNC44OSwtMTQyLjc3IDM0Ljg5LC0xMjAuMTQgMzQuODksLTEwNy44NyIvPgo8L2c+CjwhLS0gZTAmIzQ1OyZndDtuMiAtLT4KPGcgaWQ9ImVkZ2UyIiBjbGFzcz0iZWRnZSI+Cjx0aXRsZT5lMCYjNDU7Jmd0O24yPC90aXRsZT4KPHBhdGggZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgZD0iTTM0Ljg5LC0yNDkuOTJDMzQuODksLTI0OS45MiAzNC44OSwtMjA3Ljk0IDM0Ljg5LC0yMDcuOTQiLz4KPHBvbHlnb24gZmlsbD0iIzAwMDAwMCIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgcG9pbnRzPSIzNy41MiwtMjA3Ljk0IDM0Ljg5LC0yMDAuNDQgMzIuMjcsLTIwNy45NCAzNy41MiwtMjA3Ljk0Ii8+CjwvZz4KPCEtLSBlMSYjNDU7Jmd0O24zIC0tPgo8ZyBpZD0iZWRnZTQiIGNsYXNzPSJlZGdlIj4KPHRpdGxlPmUxJiM0NTsmZ3Q7bjM8L3RpdGxlPgo8cGF0aCBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMS4xIiBkPSJNMzQuODksLTkwLjI3QzM0Ljg5LC05MC4yNyAzNC44OSwtNDguMjkgMzQuODksLTQ4LjI5Ii8+Cjxwb2x5Z29uIGZpbGw9IiMwMDAwMDAiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxLjEiIHBvaW50cz0iMzcuNTIsLTQ4LjI5IDM0Ljg5LC00MC43OSAzMi4yNywtNDguMjkgMzcuNTIsLTQ4LjI5Ii8+CjwvZz4KPCEtLSBlMiYjNDU7Jmd0O24xIC0tPgo8ZyBpZD0iZWRnZTYiIGNsYXNzPSJlZGdlIj4KPHRpdGxlPmUyJiM0NTsmZ3Q7bjE8L3RpdGxlPgo8cGF0aCBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMS4xIiBkPSJNMzQuODksLTQyMy42OUMzNC44OSwtNDIzLjY5IDM0Ljg5LC0zODEuNjcgMzQuODksLTM4MS42NyIvPgo8cG9seWdvbiBmaWxsPSIjMDAwMDAwIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMS4xIiBwb2ludHM9IjM3LjUyLC0zODEuNjcgMzQuODksLTM3NC4xNyAzMi4yNywtMzgxLjY3IDM3LjUyLC0zODEuNjciLz4KPC9nPgo8IS0tIG4wIC0tPgo8ZyBpZD0ibm9kZTEiIGNsYXNzPSJub2RlIj4KPHRpdGxlPm4wPC90aXRsZT4KPHBvbHlnb24gZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgcG9pbnRzPSI2OS43OCwtNTMxLjc4IDAsLTUzMS43OCAwLC00OTMuMiA2OS43OCwtNDkzLjIgNjkuNzgsLTUzMS43OCIvPgo8dGV4dCB4bWw6c3BhY2U9InByZXNlcnZlIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiB4PSIzNC44OSIgeT0iLTUxNS4zNCIgZm9udC1mYW1pbHk9IkNvdXJpZXIsbW9ub3NwYWNlIiBmb250LXNpemU9IjEyLjAwIiBmaWxsPSIjMDAwMDAwIj5yb290PC90ZXh0Pgo8dGV4dCB4bWw6c3BhY2U9InByZXNlcnZlIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiB4PSIzNC44OSIgeT0iLTUwMS4wOSIgZm9udC1mYW1pbHk9IkNvdXJpZXIsbW9ub3NwYWNlIiBmb250LXNpemU9IjEyLjAwIiBmaWxsPSIjMDAwMDAwIj5vZmY9MHgwPC90ZXh0Pgo8L2c+CjwhLS0gbjEgLS0+CjxnIGlkPSJub2RlMiIgY2xhc3M9Im5vZGUiPgo8dGl0bGU+bjE8L3RpdGxlPgo8cG9seWdvbiBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMS4xIiBwb2ludHM9IjY5Ljc4LC0zNzIuMTMgMCwtMzcyLjEzIDAsLTMxOS4zIDY5Ljc4LC0zMTkuMyA2OS43OCwtMzcyLjEzIi8+Cjx0ZXh0IHhtbDpzcGFjZT0icHJlc2VydmUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIHg9IjM0Ljg5IiB5PSItMzU1LjY5IiBmb250LWZhbWlseT0iQ291cmllcixtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMTIuMDAiIGZpbGw9IiMwMDAwMDAiPm1ldGhvZDwvdGV4dD4KPHRleHQgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iMzQuODkiIHk9Ii0zNDEuNDQiIGZvbnQtZmFtaWx5PSJDb3VyaWVyLG1vbm9zcGFjZSIgZm9udC1zaXplPSIxMi4wMCIgZmlsbD0iIzAwMDAwMCI+TVRIMDwvdGV4dD4KPHRleHQgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iMzQuODkiIHk9Ii0zMjcuMTkiIGZvbnQtZmFtaWx5PSJDb3VyaWVyLG1vbm9zcGFjZSIgZm9udC1zaXplPSIxMi4wMCIgZmlsbD0iIzAwMDAwMCI+b2ZmPTB4MDwvdGV4dD4KPC9nPgo8IS0tIG4yIC0tPgo8ZyBpZD0ibm9kZTMiIGNsYXNzPSJub2RlIj4KPHRpdGxlPm4yPC90aXRsZT4KPHBvbHlnb24gZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgcG9pbnRzPSI2OS43OCwtMTk4LjIzIDAsLTE5OC4yMyAwLC0xNTkuNjUgNjkuNzgsLTE1OS42NSA2OS43OCwtMTk4LjIzIi8+Cjx0ZXh0IHhtbDpzcGFjZT0icHJlc2VydmUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIHg9IjM0Ljg5IiB5PSItMTgxLjc5IiBmb250LWZhbWlseT0iQ291cmllcixtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMTIuMDAiIGZpbGw9IiMwMDAwMDAiPnN0b3JlPC90ZXh0Pgo8dGV4dCB4bWw6c3BhY2U9InByZXNlcnZlIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiB4PSIzNC44OSIgeT0iLTE2Ny41NCIgZm9udC1mYW1pbHk9IkNvdXJpZXIsbW9ub3NwYWNlIiBmb250LXNpemU9IjEyLjAwIiBmaWxsPSIjMDAwMDAwIj5vZmY9MHg3PC90ZXh0Pgo8L2c+CjwhLS0gZTAgLS0+CjxnIGlkPSJub2RlNCIgY2xhc3M9Im5vZGUiPgo8dGl0bGU+ZTA8L3RpdGxlPgo8cG9seWdvbiBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMS4xIiBwb2ludHM9IjUyLjcxLC0yNjcuMyAxNy4wNywtMjY3LjMgMTcuMDcsLTI1MC4yMyA1Mi43MSwtMjUwLjIzIDUyLjcxLC0yNjcuMyIvPgo8dGV4dCB4bWw6c3BhY2U9InByZXNlcnZlIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiB4PSIzNC44OSIgeT0iLTI1NS42NCIgZm9udC1mYW1pbHk9IlRpbWVzLHNlcmlmIiBmb250LXNpemU9IjEwLjAwIiBmaWxsPSIjMDAwMDAwIj5lbnRyeTwvdGV4dD4KPC9nPgo8IS0tIG4zIC0tPgo8ZyBpZD0ibm9kZTUiIGNsYXNzPSJub2RlIj4KPHRpdGxlPm4zPC90aXRsZT4KPHBvbHlnb24gZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgcG9pbnRzPSI2OS43OCwtMzguNTggMCwtMzguNTggMCwwIDY5Ljc4LDAgNjkuNzgsLTM4LjU4Ii8+Cjx0ZXh0IHhtbDpzcGFjZT0icHJlc2VydmUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIHg9IjM0Ljg5IiB5PSItMjIuMTQiIGZvbnQtZmFtaWx5PSJDb3VyaWVyLG1vbm9zcGFjZSIgZm9udC1zaXplPSIxMi4wMCIgZmlsbD0iIzAwMDAwMCI+cmV0dXJuPC90ZXh0Pgo8dGV4dCB4bWw6c3BhY2U9InByZXNlcnZlIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiB4PSIzNC44OSIgeT0iLTcuODkiIGZvbnQtZmFtaWx5PSJDb3VyaWVyLG1vbm9zcGFjZSIgZm9udC1zaXplPSIxMi4wMCIgZmlsbD0iIzAwMDAwMCI+b2ZmPTB4YjwvdGV4dD4KPC9nPgo8IS0tIGUxIC0tPgo8ZyBpZD0ibm9kZTYiIGNsYXNzPSJub2RlIj4KPHRpdGxlPmUxPC90aXRsZT4KPHBvbHlnb24gZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgcG9pbnRzPSI1MC40NiwtMTA3LjY1IDE5LjMyLC0xMDcuNjUgMTkuMzIsLTkwLjU4IDUwLjQ2LC05MC41OCA1MC40NiwtMTA3LjY1Ii8+Cjx0ZXh0IHhtbDpzcGFjZT0icHJlc2VydmUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIHg9IjM0Ljg5IiB5PSItOTUuOTkiIGZvbnQtZmFtaWx5PSJUaW1lcyxzZXJpZiIgZm9udC1zaXplPSIxMC4wMCIgZmlsbD0iIzAwMDAwMCI+bmV4dDwvdGV4dD4KPC9nPgo8IS0tIGUyIC0tPgo8ZyBpZD0ibm9kZTciIGNsYXNzPSJub2RlIj4KPHRpdGxlPmUyPC90aXRsZT4KPHBvbHlnb24gZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgcG9pbnRzPSI1Mi43MSwtNDQxLjIgMTcuMDcsLTQ0MS4yIDE3LjA3LC00MjQuMTMgNTIuNzEsLTQyNC4xMyA1Mi43MSwtNDQxLjIiLz4KPHRleHQgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iMzQuODkiIHk9Ii00MjkuNTQiIGZvbnQtZmFtaWx5PSJUaW1lcyxzZXJpZiIgZm9udC1zaXplPSIxMC4wMCIgZmlsbD0iIzAwMDAwMCI+ZW50cnk8L3RleHQ+CjwvZz4KPC9nPgo8L3N2Zz4K" x="95" y="170" width="205" height="960" preserveAspectRatio="xMidYMid meet"/> 4 + <image href="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIKICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPgo8IS0tIEdlbmVyYXRlZCBieSBncmFwaHZpeiB2ZXJzaW9uIDE0LjEuNSAoMCkKIC0tPgo8IS0tIFRpdGxlOiB0b2JpX2NmZyBQYWdlczogMSAtLT4KPHN2ZyB3aWR0aD0iMzIycHQiIGhlaWdodD0iNTU4cHQiCiB2aWV3Qm94PSIwLjAwIDAuMDAgMzIyLjAwIDU1OC4wMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CjxnIGlkPSJncmFwaDAiIGNsYXNzPSJncmFwaCIgdHJhbnNmb3JtPSJzY2FsZSgxIDEpIHJvdGF0ZSgwKSB0cmFuc2xhdGUoMTIuOTYgNTQ0Ljc0KSI+Cjx0aXRsZT50b2JpX2NmZzwvdGl0bGU+Cjxwb2x5Z29uIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0ibm9uZSIgcG9pbnRzPSItMTIuOTYsMTIuOTYgLTEyLjk2LC01NDQuNzQgMzA5LjQ5LC01NDQuNzQgMzA5LjQ5LDEyLjk2IC0xMi45NiwxMi45NiIvPgo8IS0tIG4wJiM0NTsmZ3Q7ZTQgLS0+CjxnIGlkPSJlZGdlOSIgY2xhc3M9ImVkZ2UiPgo8dGl0bGU+bjAmIzQ1OyZndDtlNDwvdGl0bGU+CjxwYXRoIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxLjEiIGQ9Ik0xNDQuODksLTQ5Mi43NkMxNDQuODksLTQ3Ni4zMiAxNDQuODksLTQ1My42OSAxNDQuODksLTQ0MS40MiIvPgo8L2c+CjwhLS0gbjEmIzQ1OyZndDtlMiAtLT4KPGcgaWQ9ImVkZ2U1IiBjbGFzcz0iZWRnZSI+Cjx0aXRsZT5uMSYjNDU7Jmd0O2UyPC90aXRsZT4KPHBhdGggZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgZD0iTTE0NC44OSwtMzE5LjE1QzE0NC44OSwtMzAxLjU4IDE0NC44OSwtMjc5LjYyIDE0NC44OSwtMjY3LjYyIi8+CjwvZz4KPCEtLSBuMiYjNDU7Jmd0O2UwIC0tPgo8ZyBpZD0iZWRnZTEiIGNsYXNzPSJlZGdlIj4KPHRpdGxlPm4yJiM0NTsmZ3Q7ZTA8L3RpdGxlPgo8cGF0aCBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMS4xIiBkPSJNMTIwLjIyLC0xNTkuMThDMTIwLjIyLC0xMzUuNTEgMTIwLjIyLC05OSAxMjAuMjIsLTk5IDEyMC4yMiwtOTkgOTEuOTYsLTk5IDczLjIsLTk5Ii8+CjwvZz4KPCEtLSBuMiYjNDU7Jmd0O2UxIC0tPgo8ZyBpZD0iZWRnZTMiIGNsYXNzPSJlZGdlIj4KPHRpdGxlPm4yJiM0NTsmZ3Q7ZTE8L3RpdGxlPgo8cGF0aCBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMS4xIiBkPSJNMTQ0Ljg5LC0xNTkuMjFDMTQ0Ljg5LC0xNDIuNzcgMTQ0Ljg5LC0xMjAuMTQgMTQ0Ljg5LC0xMDcuODciLz4KPC9nPgo8IS0tIG4yJiM0NTsmZ3Q7ZTMgLS0+CjxnIGlkPSJlZGdlNyIgY2xhc3M9ImVkZ2UiPgo8dGl0bGU+bjImIzQ1OyZndDtlMzwvdGl0bGU+CjxwYXRoIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxLjEiIGQ9Ik0xNjkuNTYsLTE1OS4xOEMxNjkuNTYsLTEzNS41MSAxNjkuNTYsLTk5IDE2OS41NiwtOTkgMTY5LjU2LC05OSAxOTguOTQsLTk5IDIxNy45OSwtOTkiLz4KPC9nPgo8IS0tIGUwJiM0NTsmZ3Q7bjMgLS0+CjxnIGlkPSJlZGdlMiIgY2xhc3M9ImVkZ2UiPgo8dGl0bGU+ZTAmIzQ1OyZndDtuMzwvdGl0bGU+CjxwYXRoIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxLjEiIGQ9Ik01NS4zNiwtOTAuMjdDNTUuMzYsLTkwLjI3IDU1LjM2LC00OC4yOSA1NS4zNiwtNDguMjkiLz4KPHBvbHlnb24gZmlsbD0iIzAwMDAwMCIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgcG9pbnRzPSI1Ny45OSwtNDguMjkgNTUuMzYsLTQwLjc5IDUyLjc0LC00OC4yOSA1Ny45OSwtNDguMjkiLz4KPC9nPgo8IS0tIGUxJiM0NTsmZ3Q7bjQgLS0+CjxnIGlkPSJlZGdlNCIgY2xhc3M9ImVkZ2UiPgo8dGl0bGU+ZTEmIzQ1OyZndDtuNDwvdGl0bGU+CjxwYXRoIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxLjEiIGQ9Ik0xNDQuODksLTkwLjI3QzE0NC44OSwtOTAuMjcgMTQ0Ljg5LC00OC4yOSAxNDQuODksLTQ4LjI5Ii8+Cjxwb2x5Z29uIGZpbGw9IiMwMDAwMDAiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxLjEiIHBvaW50cz0iMTQ3LjUyLC00OC4yOSAxNDQuODksLTQwLjc5IDE0Mi4yNywtNDguMjkgMTQ3LjUyLC00OC4yOSIvPgo8L2c+CjwhLS0gZTImIzQ1OyZndDtuMiAtLT4KPGcgaWQ9ImVkZ2U2IiBjbGFzcz0iZWRnZSI+Cjx0aXRsZT5lMiYjNDU7Jmd0O24yPC90aXRsZT4KPHBhdGggZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgZD0iTTE0NC44OSwtMjQ5LjkyQzE0NC44OSwtMjQ5LjkyIDE0NC44OSwtMjA3Ljk0IDE0NC44OSwtMjA3Ljk0Ii8+Cjxwb2x5Z29uIGZpbGw9IiMwMDAwMDAiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxLjEiIHBvaW50cz0iMTQ3LjUyLC0yMDcuOTQgMTQ0Ljg5LC0yMDAuNDQgMTQyLjI3LC0yMDcuOTQgMTQ3LjUyLC0yMDcuOTQiLz4KPC9nPgo8IS0tIGUzJiM0NTsmZ3Q7bjUgLS0+CjxnIGlkPSJlZGdlOCIgY2xhc3M9ImVkZ2UiPgo8dGl0bGU+ZTMmIzQ1OyZndDtuNTwvdGl0bGU+CjxwYXRoIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxLjEiIGQ9Ik0yMzQuMzYsLTkwLjI3QzIzNC4zNiwtOTAuMjcgMjM0LjM2LC00OC4yOSAyMzQuMzYsLTQ4LjI5Ii8+Cjxwb2x5Z29uIGZpbGw9IiMwMDAwMDAiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxLjEiIHBvaW50cz0iMjM2Ljk4LC00OC4yOSAyMzQuMzYsLTQwLjc5IDIzMS43MywtNDguMjkgMjM2Ljk4LC00OC4yOSIvPgo8L2c+CjwhLS0gZTQmIzQ1OyZndDtuMSAtLT4KPGcgaWQ9ImVkZ2UxMCIgY2xhc3M9ImVkZ2UiPgo8dGl0bGU+ZTQmIzQ1OyZndDtuMTwvdGl0bGU+CjxwYXRoIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxLjEiIGQ9Ik0xNDQuODksLTQyMy42OUMxNDQuODksLTQyMy42OSAxNDQuODksLTM4MS42NyAxNDQuODksLTM4MS42NyIvPgo8cG9seWdvbiBmaWxsPSIjMDAwMDAwIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMS4xIiBwb2ludHM9IjE0Ny41MiwtMzgxLjY3IDE0NC44OSwtMzc0LjE3IDE0Mi4yNywtMzgxLjY3IDE0Ny41MiwtMzgxLjY3Ii8+CjwvZz4KPCEtLSBuMCAtLT4KPGcgaWQ9Im5vZGUxIiBjbGFzcz0ibm9kZSI+Cjx0aXRsZT5uMDwvdGl0bGU+Cjxwb2x5Z29uIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxLjEiIHBvaW50cz0iMTc5Ljc4LC01MzEuNzggMTEwLC01MzEuNzggMTEwLC00OTMuMiAxNzkuNzgsLTQ5My4yIDE3OS43OCwtNTMxLjc4Ii8+Cjx0ZXh0IHhtbDpzcGFjZT0icHJlc2VydmUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIHg9IjE0NC44OSIgeT0iLTUxNS4zNCIgZm9udC1mYW1pbHk9IkNvdXJpZXIsbW9ub3NwYWNlIiBmb250LXNpemU9IjEyLjAwIiBmaWxsPSIjMDAwMDAwIj5yb290PC90ZXh0Pgo8dGV4dCB4bWw6c3BhY2U9InByZXNlcnZlIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiB4PSIxNDQuODkiIHk9Ii01MDEuMDkiIGZvbnQtZmFtaWx5PSJDb3VyaWVyLG1vbm9zcGFjZSIgZm9udC1zaXplPSIxMi4wMCIgZmlsbD0iIzAwMDAwMCI+b2ZmPTB4MDwvdGV4dD4KPC9nPgo8IS0tIG4xIC0tPgo8ZyBpZD0ibm9kZTIiIGNsYXNzPSJub2RlIj4KPHRpdGxlPm4xPC90aXRsZT4KPHBvbHlnb24gZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgcG9pbnRzPSIxNzkuNzgsLTM3Mi4xMyAxMTAsLTM3Mi4xMyAxMTAsLTMxOS4zIDE3OS43OCwtMzE5LjMgMTc5Ljc4LC0zNzIuMTMiLz4KPHRleHQgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iMTQ0Ljg5IiB5PSItMzU1LjY5IiBmb250LWZhbWlseT0iQ291cmllcixtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMTIuMDAiIGZpbGw9IiMwMDAwMDAiPm1ldGhvZDwvdGV4dD4KPHRleHQgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iMTQ0Ljg5IiB5PSItMzQxLjQ0IiBmb250LWZhbWlseT0iQ291cmllcixtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMTIuMDAiIGZpbGw9IiMwMDAwMDAiPklGRTA8L3RleHQ+Cjx0ZXh0IHhtbDpzcGFjZT0icHJlc2VydmUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIHg9IjE0NC44OSIgeT0iLTMyNy4xOSIgZm9udC1mYW1pbHk9IkNvdXJpZXIsbW9ub3NwYWNlIiBmb250LXNpemU9IjEyLjAwIiBmaWxsPSIjMDAwMDAwIj5vZmY9MHgwPC90ZXh0Pgo8L2c+CjwhLS0gbjIgLS0+CjxnIGlkPSJub2RlMyIgY2xhc3M9Im5vZGUiPgo8dGl0bGU+bjI8L3RpdGxlPgo8cG9seWdvbiBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMS4xIiBwb2ludHM9IjE3OS43OCwtMTk4LjIzIDExMCwtMTk4LjIzIDExMCwtMTU5LjY1IDE3OS43OCwtMTU5LjY1IDE3OS43OCwtMTk4LjIzIi8+Cjx0ZXh0IHhtbDpzcGFjZT0icHJlc2VydmUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIHg9IjE0NC44OSIgeT0iLTE4MS43OSIgZm9udC1mYW1pbHk9IkNvdXJpZXIsbW9ub3NwYWNlIiBmb250LXNpemU9IjEyLjAwIiBmaWxsPSIjMDAwMDAwIj5pZjwvdGV4dD4KPHRleHQgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iMTQ0Ljg5IiB5PSItMTY3LjU0IiBmb250LWZhbWlseT0iQ291cmllcixtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMTIuMDAiIGZpbGw9IiMwMDAwMDAiPm9mZj0weDc8L3RleHQ+CjwvZz4KPCEtLSBuMyAtLT4KPGcgaWQ9Im5vZGU0IiBjbGFzcz0ibm9kZSI+Cjx0aXRsZT5uMzwvdGl0bGU+Cjxwb2x5Z29uIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxLjEiIHBvaW50cz0iNjkuNzgsLTM4LjU4IDAsLTM4LjU4IDAsMCA2OS43OCwwIDY5Ljc4LC0zOC41OCIvPgo8dGV4dCB4bWw6c3BhY2U9InByZXNlcnZlIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiB4PSIzNC44OSIgeT0iLTIyLjE0IiBmb250LWZhbWlseT0iQ291cmllcixtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMTIuMDAiIGZpbGw9IiMwMDAwMDAiPnN0b3JlPC90ZXh0Pgo8dGV4dCB4bWw6c3BhY2U9InByZXNlcnZlIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiB4PSIzNC44OSIgeT0iLTcuODkiIGZvbnQtZmFtaWx5PSJDb3VyaWVyLG1vbm9zcGFjZSIgZm9udC1zaXplPSIxMi4wMCIgZmlsbD0iIzAwMDAwMCI+b2ZmPTB4YTwvdGV4dD4KPC9nPgo8IS0tIGUwIC0tPgo8ZyBpZD0ibm9kZTUiIGNsYXNzPSJub2RlIj4KPHRpdGxlPmUwPC90aXRsZT4KPHBvbHlnb24gZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgcG9pbnRzPSI3Mi44NCwtMTA3LjY1IDQwLjk1LC0xMDcuNjUgNDAuOTUsLTkwLjU4IDcyLjg0LC05MC41OCA3Mi44NCwtMTA3LjY1Ii8+Cjx0ZXh0IHhtbDpzcGFjZT0icHJlc2VydmUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIHg9IjU2Ljg5IiB5PSItOTUuOTkiIGZvbnQtZmFtaWx5PSJUaW1lcyxzZXJpZiIgZm9udC1zaXplPSIxMC4wMCIgZmlsbD0iIzAwMDAwMCI+dGhlbjwvdGV4dD4KPC9nPgo8IS0tIG40IC0tPgo8ZyBpZD0ibm9kZTYiIGNsYXNzPSJub2RlIj4KPHRpdGxlPm40PC90aXRsZT4KPHBvbHlnb24gZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgcG9pbnRzPSIxODMuNTMsLTM4LjU4IDEwNi4yNSwtMzguNTggMTA2LjI1LDAgMTgzLjUzLDAgMTgzLjUzLC0zOC41OCIvPgo8dGV4dCB4bWw6c3BhY2U9InByZXNlcnZlIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiB4PSIxNDQuODkiIHk9Ii0yMi4xNCIgZm9udC1mYW1pbHk9IkNvdXJpZXIsbW9ub3NwYWNlIiBmb250LXNpemU9IjEyLjAwIiBmaWxsPSIjMDAwMDAwIj5zdG9yZTwvdGV4dD4KPHRleHQgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iMTQ0Ljg5IiB5PSItNy44OSIgZm9udC1mYW1pbHk9IkNvdXJpZXIsbW9ub3NwYWNlIiBmb250LXNpemU9IjEyLjAwIiBmaWxsPSIjMDAwMDAwIj5vZmY9MHgxMDwvdGV4dD4KPC9nPgo8IS0tIGUxIC0tPgo8ZyBpZD0ibm9kZTciIGNsYXNzPSJub2RlIj4KPHRpdGxlPmUxPC90aXRsZT4KPHBvbHlnb24gZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgcG9pbnRzPSIxNTkuMzMsLTEwNy42NSAxMzAuNDQsLTEwNy42NSAxMzAuNDQsLTkwLjU4IDE1OS4zMywtOTAuNTggMTU5LjMzLC0xMDcuNjUiLz4KPHRleHQgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iMTQ0Ljg5IiB5PSItOTUuOTkiIGZvbnQtZmFtaWx5PSJUaW1lcyxzZXJpZiIgZm9udC1zaXplPSIxMC4wMCIgZmlsbD0iIzAwMDAwMCI+ZWxzZTwvdGV4dD4KPC9nPgo8IS0tIGUyIC0tPgo8ZyBpZD0ibm9kZTgiIGNsYXNzPSJub2RlIj4KPHRpdGxlPmUyPC90aXRsZT4KPHBvbHlnb24gZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgcG9pbnRzPSIxNjIuNzEsLTI2Ny4zIDEyNy4wNywtMjY3LjMgMTI3LjA3LC0yNTAuMjMgMTYyLjcxLC0yNTAuMjMgMTYyLjcxLC0yNjcuMyIvPgo8dGV4dCB4bWw6c3BhY2U9InByZXNlcnZlIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiB4PSIxNDQuODkiIHk9Ii0yNTUuNjQiIGZvbnQtZmFtaWx5PSJUaW1lcyxzZXJpZiIgZm9udC1zaXplPSIxMC4wMCIgZmlsbD0iIzAwMDAwMCI+ZW50cnk8L3RleHQ+CjwvZz4KPCEtLSBuNSAtLT4KPGcgaWQ9Im5vZGU5IiBjbGFzcz0ibm9kZSI+Cjx0aXRsZT5uNTwvdGl0bGU+Cjxwb2x5Z29uIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxLjEiIHBvaW50cz0iMjk2LjUzLC0zOC41OCAyMTkuMjUsLTM4LjU4IDIxOS4yNSwwIDI5Ni41MywwIDI5Ni41MywtMzguNTgiLz4KPHRleHQgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iMjU3Ljg5IiB5PSItMjIuMTQiIGZvbnQtZmFtaWx5PSJDb3VyaWVyLG1vbm9zcGFjZSIgZm9udC1zaXplPSIxMi4wMCIgZmlsbD0iIzAwMDAwMCI+cmV0dXJuPC90ZXh0Pgo8dGV4dCB4bWw6c3BhY2U9InByZXNlcnZlIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiB4PSIyNTcuODkiIHk9Ii03Ljg5IiBmb250LWZhbWlseT0iQ291cmllcixtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMTIuMDAiIGZpbGw9IiMwMDAwMDAiPm9mZj0weDE0PC90ZXh0Pgo8L2c+CjwhLS0gZTMgLS0+CjxnIGlkPSJub2RlMTAiIGNsYXNzPSJub2RlIj4KPHRpdGxlPmUzPC90aXRsZT4KPHBvbHlnb24gZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgcG9pbnRzPSIyNDkuNDYsLTEwNy42NSAyMTguMzIsLTEwNy42NSAyMTguMzIsLTkwLjU4IDI0OS40NiwtOTAuNTggMjQ5LjQ2LC0xMDcuNjUiLz4KPHRleHQgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iMjMzLjg5IiB5PSItOTUuOTkiIGZvbnQtZmFtaWx5PSJUaW1lcyxzZXJpZiIgZm9udC1zaXplPSIxMC4wMCIgZmlsbD0iIzAwMDAwMCI+bmV4dDwvdGV4dD4KPC9nPgo8IS0tIGU0IC0tPgo8ZyBpZD0ibm9kZTExIiBjbGFzcz0ibm9kZSI+Cjx0aXRsZT5lNDwvdGl0bGU+Cjxwb2x5Z29uIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxLjEiIHBvaW50cz0iMTYyLjcxLC00NDEuMiAxMjcuMDcsLTQ0MS4yIDEyNy4wNywtNDI0LjEzIDE2Mi43MSwtNDI0LjEzIDE2Mi43MSwtNDQxLjIiLz4KPHRleHQgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iMTQ0Ljg5IiB5PSItNDI5LjU0IiBmb250LWZhbWlseT0iVGltZXMsc2VyaWYiIGZvbnQtc2l6ZT0iMTAuMDAiIGZpbGw9IiMwMDAwMDAiPmVudHJ5PC90ZXh0Pgo8L2c+CjwvZz4KPC9zdmc+Cg==" x="400" y="170" width="555" height="960" preserveAspectRatio="xMidYMid meet"/> 5 + <image href="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIKICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPgo8IS0tIEdlbmVyYXRlZCBieSBncmFwaHZpeiB2ZXJzaW9uIDE0LjEuNSAoMCkKIC0tPgo8IS0tIFRpdGxlOiB0b2JpX2NmZyBQYWdlczogMSAtLT4KPHN2ZyB3aWR0aD0iMzU0cHQiIGhlaWdodD0iOTYwcHQiCiB2aWV3Qm94PSIwLjAwIDAuMDAgMzU0LjAwIDk2MC4wMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CjxnIGlkPSJncmFwaDAiIGNsYXNzPSJncmFwaCIgdHJhbnNmb3JtPSJzY2FsZSgxIDEpIHJvdGF0ZSgwKSB0cmFuc2xhdGUoMTIuOTYgOTQ3LjM2KSI+Cjx0aXRsZT50b2JpX2NmZzwvdGl0bGU+Cjxwb2x5Z29uIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0ibm9uZSIgcG9pbnRzPSItMTIuOTYsMTIuOTYgLTEyLjk2LC05NDcuMzYgMzQwLjkyLC05NDcuMzYgMzQwLjkyLDEyLjk2IC0xMi45NiwxMi45NiIvPgo8IS0tIG4wJiM0NTsmZ3Q7ZTggLS0+CjxnIGlkPSJlZGdlMTciIGNsYXNzPSJlZGdlIj4KPHRpdGxlPm4wJiM0NTsmZ3Q7ZTg8L3RpdGxlPgo8cGF0aCBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMS4xIiBkPSJNMjYzLjY0LC04OTUuMzhDMjYzLjY0LC04NzguOTQgMjYzLjY0LC04NTYuMzEgMjYzLjY0LC04NDQuMDQiLz4KPC9nPgo8IS0tIG4xJiM0NTsmZ3Q7ZTAgLS0+CjxnIGlkPSJlZGdlMSIgY2xhc3M9ImVkZ2UiPgo8dGl0bGU+bjEmIzQ1OyZndDtlMDwvdGl0bGU+CjxwYXRoIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxLjEiIGQ9Ik0yNjMuNjQsLTcyMS43N0MyNjMuNjQsLTcwNC4yIDI2My42NCwtNjgyLjI0IDI2My42NCwtNjcwLjI0Ii8+CjwvZz4KPCEtLSBuMiYjNDU7Jmd0O2U2IC0tPgo8ZyBpZD0iZWRnZTEzIiBjbGFzcz0iZWRnZSI+Cjx0aXRsZT5uMiYjNDU7Jmd0O2U2PC90aXRsZT4KPHBhdGggZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgZD0iTTI2My42NCwtNTYxLjgzQzI2My42NCwtNTQ1LjM5IDI2My42NCwtNTIyLjc2IDI2My42NCwtNTEwLjQ5Ii8+CjwvZz4KPCEtLSBlMCYjNDU7Jmd0O24yIC0tPgo8ZyBpZD0iZWRnZTIiIGNsYXNzPSJlZGdlIj4KPHRpdGxlPmUwJiM0NTsmZ3Q7bjI8L3RpdGxlPgo8cGF0aCBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMS4xIiBkPSJNMjYzLjY0LC02NTIuNTRDMjYzLjY0LC02NTIuNTQgMjYzLjY0LC02MTAuNTYgMjYzLjY0LC02MTAuNTYiLz4KPHBvbHlnb24gZmlsbD0iIzAwMDAwMCIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgcG9pbnRzPSIyNjYuMjcsLTYxMC41NiAyNjMuNjQsLTYwMy4wNiAyNjEuMDIsLTYxMC41NiAyNjYuMjcsLTYxMC41NiIvPgo8L2c+CjwhLS0gbjMmIzQ1OyZndDtlMyAtLT4KPGcgaWQ9ImVkZ2U3IiBjbGFzcz0iZWRnZSI+Cjx0aXRsZT5uMyYjNDU7Jmd0O2UzPC90aXRsZT4KPHBhdGggZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgZD0iTTIzOC40MSwtNDAyLjE2QzIzOC40MSwtMzc4LjQ5IDIzOC40MSwtMzQyIDIzOC40MSwtMzQyIDIzOC40MSwtMzQyIDIxOC40NSwtMzQyIDE5OS41OCwtMzQyIi8+CjwvZz4KPCEtLSBuMyYjNDU7Jmd0O2U3IC0tPgo8ZyBpZD0iZWRnZTE1IiBjbGFzcz0iZWRnZSI+Cjx0aXRsZT5uMyYjNDU7Jmd0O2U3PC90aXRsZT4KPHBhdGggZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgZD0iTTI2My42NCwtNDAyLjE4QzI2My42NCwtMzg1Ljc0IDI2My42NCwtMzYzLjExIDI2My42NCwtMzUwLjg0Ii8+CjwvZz4KPCEtLSBuNCYjNDU7Jmd0O2UxIC0tPgo8ZyBpZD0iZWRnZTMiIGNsYXNzPSJlZGdlIj4KPHRpdGxlPm40JiM0NTsmZ3Q7ZTE8L3RpdGxlPgo8cGF0aCBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMS4xIiBkPSJNMTI3LjQ3LC0yNDIuNDJDMTI3LjQ3LC0yMTguNjUgMTI3LjQ3LC0xODIgMTI3LjQ3LC0xODIgMTI3LjQ3LC0xODIgOTguMjEsLTE4MiA3OSwtMTgyIi8+CjwvZz4KPCEtLSBuNCYjNDU7Jmd0O2UyIC0tPgo8ZyBpZD0iZWRnZTUiIGNsYXNzPSJlZGdlIj4KPHRpdGxlPm40JiM0NTsmZ3Q7ZTI8L3RpdGxlPgo8cGF0aCBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMS4xIiBkPSJNMTUxLjY0LC0yNDIuNTNDMTUxLjY0LC0yMjYuMDkgMTUxLjY0LC0yMDMuNDYgMTUxLjY0LC0xOTEuMTkiLz4KPC9nPgo8IS0tIG40JiM0NTsmZ3Q7ZTQgLS0+CjxnIGlkPSJlZGdlOSIgY2xhc3M9ImVkZ2UiPgo8dGl0bGU+bjQmIzQ1OyZndDtlNDwvdGl0bGU+CjxwYXRoIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxLjEiIGQ9Ik0xNzYuODEsLTI0Mi40MkMxNzYuODEsLTIxOC42NSAxNzYuODEsLTE4MiAxNzYuODEsLTE4MiAxNzYuODEsLTE4MiAyMDYuNDIsLTE4MiAyMjUuNjEsLTE4MiIvPgo8L2c+CjwhLS0gZTEmIzQ1OyZndDtuNSAtLT4KPGcgaWQ9ImVkZ2U0IiBjbGFzcz0iZWRnZSI+Cjx0aXRsZT5lMSYjNDU7Jmd0O241PC90aXRsZT4KPHBhdGggZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgZD0iTTYxLjk5LC0xNzMuNDZDNjEuOTksLTE3My40NiA2MS45OSwtMTI0LjE1IDYxLjk5LC0xMjQuMTUiLz4KPHBvbHlnb24gZmlsbD0iIzAwMDAwMCIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgcG9pbnRzPSI2NC42MSwtMTI0LjE1IDYxLjk5LC0xMTYuNjUgNTkuMzYsLTEyNC4xNSA2NC42MSwtMTI0LjE1Ii8+CjwvZz4KPCEtLSBlMiYjNDU7Jmd0O242IC0tPgo8ZyBpZD0iZWRnZTYiIGNsYXNzPSJlZGdlIj4KPHRpdGxlPmUyJiM0NTsmZ3Q7bjY8L3RpdGxlPgo8cGF0aCBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMS4xIiBkPSJNMTUxLjY0LC0xNzMuNDZDMTUxLjY0LC0xNzMuNDYgMTUxLjY0LC0xMjQuMTUgMTUxLjY0LC0xMjQuMTUiLz4KPHBvbHlnb24gZmlsbD0iIzAwMDAwMCIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgcG9pbnRzPSIxNTQuMjcsLTEyNC4xNSAxNTEuNjQsLTExNi42NSAxNDkuMDIsLTEyNC4xNSAxNTQuMjcsLTEyNC4xNSIvPgo8L2c+CjwhLS0gZTMmIzQ1OyZndDtuNCAtLT4KPGcgaWQ9ImVkZ2U4IiBjbGFzcz0iZWRnZSI+Cjx0aXRsZT5lMyYjNDU7Jmd0O240PC90aXRsZT4KPHBhdGggZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgZD0iTTE2MC44NiwtMzMzLjI0QzE2MC44NiwtMzMzLjI0IDE2MC44NiwtMjkxLjI2IDE2MC44NiwtMjkxLjI2Ii8+Cjxwb2x5Z29uIGZpbGw9IiMwMDAwMDAiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxLjEiIHBvaW50cz0iMTYzLjQ5LC0yOTEuMjYgMTYwLjg2LC0yODMuNzYgMTU4LjI0LC0yOTEuMjYgMTYzLjQ5LC0yOTEuMjYiLz4KPC9nPgo8IS0tIG43JiM0NTsmZ3Q7ZTUgLS0+CjxnIGlkPSJlZGdlMTEiIGNsYXNzPSJlZGdlIj4KPHRpdGxlPm43JiM0NTsmZ3Q7ZTU8L3RpdGxlPgo8cGF0aCBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMS4xIiBkPSJNMjk5LjMsLTY4LjkyQzI5OS4zLC01MS4zNSAyOTkuMywtMjkuMzkgMjk5LjMsLTE3LjM5Ii8+CjwvZz4KPCEtLSBlNCYjNDU7Jmd0O243IC0tPgo8ZyBpZD0iZWRnZTEwIiBjbGFzcz0iZWRnZSI+Cjx0aXRsZT5lNCYjNDU7Jmd0O243PC90aXRsZT4KPHBhdGggZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgZD0iTTI0MS42NCwtMTczLjQ2QzI0MS42NCwtMTczLjQ2IDI0MS42NCwtMTMxLjQ0IDI0MS42NCwtMTMxLjQ0Ii8+Cjxwb2x5Z29uIGZpbGw9IiMwMDAwMDAiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxLjEiIHBvaW50cz0iMjQ0LjI3LC0xMzEuNDQgMjQxLjY0LC0xMjMuOTQgMjM5LjAyLC0xMzEuNDQgMjQ0LjI3LC0xMzEuNDQiLz4KPC9nPgo8IS0tIGU1JiM0NTsmZ3Q7bjMgLS0+CjxnIGlkPSJlZGdlMTIiIGNsYXNzPSJlZGdlIj4KPHRpdGxlPmU1JiM0NTsmZ3Q7bjM8L3RpdGxlPgo8cGF0aCBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMS4xIiBkPSJNMzE1LjYyLC0xNy41NkMzMTUuNjIsLTc4LjAyIDMxNS42MiwtNDIyIDMxNS42MiwtNDIyIDMxNS42MiwtNDIyIDMwOC4wNSwtNDIyIDMwOC4wNSwtNDIyIi8+Cjxwb2x5Z29uIGZpbGw9IiMwMDAwMDAiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxLjEiIHBvaW50cz0iMzA4LjA1LC00MTkuMzggMzAwLjU1LC00MjIgMzA4LjA1LC00MjQuNjMgMzA4LjA1LC00MTkuMzgiLz4KPC9nPgo8IS0tIGU2JiM0NTsmZ3Q7bjMgLS0+CjxnIGlkPSJlZGdlMTQiIGNsYXNzPSJlZGdlIj4KPHRpdGxlPmU2JiM0NTsmZ3Q7bjM8L3RpdGxlPgo8cGF0aCBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMS4xIiBkPSJNMjYzLjY0LC00OTIuODlDMjYzLjY0LC00OTIuODkgMjYzLjY0LC00NTAuOTEgMjYzLjY0LC00NTAuOTEiLz4KPHBvbHlnb24gZmlsbD0iIzAwMDAwMCIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgcG9pbnRzPSIyNjYuMjcsLTQ1MC45MSAyNjMuNjQsLTQ0My40MSAyNjEuMDIsLTQ1MC45MSAyNjYuMjcsLTQ1MC45MSIvPgo8L2c+CjwhLS0gZTcmIzQ1OyZndDtuOCAtLT4KPGcgaWQ9ImVkZ2UxNiIgY2xhc3M9ImVkZ2UiPgo8dGl0bGU+ZTcmIzQ1OyZndDtuODwvdGl0bGU+CjxwYXRoIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxLjEiIGQ9Ik0yNjMuNjQsLTMzMy4yNEMyNjMuNjQsLTMzMy4yNCAyNjMuNjQsLTI5MS4yNiAyNjMuNjQsLTI5MS4yNiIvPgo8cG9seWdvbiBmaWxsPSIjMDAwMDAwIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMS4xIiBwb2ludHM9IjI2Ni4yNywtMjkxLjI2IDI2My42NCwtMjgzLjc2IDI2MS4wMiwtMjkxLjI2IDI2Ni4yNywtMjkxLjI2Ii8+CjwvZz4KPCEtLSBlOCYjNDU7Jmd0O24xIC0tPgo8ZyBpZD0iZWRnZTE4IiBjbGFzcz0iZWRnZSI+Cjx0aXRsZT5lOCYjNDU7Jmd0O24xPC90aXRsZT4KPHBhdGggZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgZD0iTTI2My42NCwtODI2LjMxQzI2My42NCwtODI2LjMxIDI2My42NCwtNzg0LjI5IDI2My42NCwtNzg0LjI5Ii8+Cjxwb2x5Z29uIGZpbGw9IiMwMDAwMDAiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxLjEiIHBvaW50cz0iMjY2LjI3LC03ODQuMjkgMjYzLjY0LC03NzYuNzkgMjYxLjAyLC03ODQuMjkgMjY2LjI3LC03ODQuMjkiLz4KPC9nPgo8IS0tIG4wIC0tPgo8ZyBpZD0ibm9kZTEiIGNsYXNzPSJub2RlIj4KPHRpdGxlPm4wPC90aXRsZT4KPHBvbHlnb24gZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgcG9pbnRzPSIyOTguNTMsLTkzNC40IDIyOC43NSwtOTM0LjQgMjI4Ljc1LC04OTUuODIgMjk4LjUzLC04OTUuODIgMjk4LjUzLC05MzQuNCIvPgo8dGV4dCB4bWw6c3BhY2U9InByZXNlcnZlIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiB4PSIyNjMuNjQiIHk9Ii05MTcuOTYiIGZvbnQtZmFtaWx5PSJDb3VyaWVyLG1vbm9zcGFjZSIgZm9udC1zaXplPSIxMi4wMCIgZmlsbD0iIzAwMDAwMCI+cm9vdDwvdGV4dD4KPHRleHQgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iMjYzLjY0IiB5PSItOTAzLjcxIiBmb250LWZhbWlseT0iQ291cmllcixtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMTIuMDAiIGZpbGw9IiMwMDAwMDAiPm9mZj0weDA8L3RleHQ+CjwvZz4KPCEtLSBuMSAtLT4KPGcgaWQ9Im5vZGUyIiBjbGFzcz0ibm9kZSI+Cjx0aXRsZT5uMTwvdGl0bGU+Cjxwb2x5Z29uIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxLjEiIHBvaW50cz0iMjk4LjUzLC03NzQuNzUgMjI4Ljc1LC03NzQuNzUgMjI4Ljc1LC03MjEuOTIgMjk4LjUzLC03MjEuOTIgMjk4LjUzLC03NzQuNzUiLz4KPHRleHQgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iMjYzLjY0IiB5PSItNzU4LjMxIiBmb250LWZhbWlseT0iQ291cmllcixtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMTIuMDAiIGZpbGw9IiMwMDAwMDAiPm1ldGhvZDwvdGV4dD4KPHRleHQgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iMjYzLjY0IiB5PSItNzQ0LjA2IiBmb250LWZhbWlseT0iQ291cmllcixtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMTIuMDAiIGZpbGw9IiMwMDAwMDAiPldMTzA8L3RleHQ+Cjx0ZXh0IHhtbDpzcGFjZT0icHJlc2VydmUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIHg9IjI2My42NCIgeT0iLTcyOS44MSIgZm9udC1mYW1pbHk9IkNvdXJpZXIsbW9ub3NwYWNlIiBmb250LXNpemU9IjEyLjAwIiBmaWxsPSIjMDAwMDAwIj5vZmY9MHgwPC90ZXh0Pgo8L2c+CjwhLS0gbjIgLS0+CjxnIGlkPSJub2RlMyIgY2xhc3M9Im5vZGUiPgo8dGl0bGU+bjI8L3RpdGxlPgo8cG9seWdvbiBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMS4xIiBwb2ludHM9IjI5OC41MywtNjAwLjg1IDIyOC43NSwtNjAwLjg1IDIyOC43NSwtNTYyLjI3IDI5OC41MywtNTYyLjI3IDI5OC41MywtNjAwLjg1Ii8+Cjx0ZXh0IHhtbDpzcGFjZT0icHJlc2VydmUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIHg9IjI2My42NCIgeT0iLTU4NC40MSIgZm9udC1mYW1pbHk9IkNvdXJpZXIsbW9ub3NwYWNlIiBmb250LXNpemU9IjEyLjAwIiBmaWxsPSIjMDAwMDAwIj5zdG9yZTwvdGV4dD4KPHRleHQgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iMjYzLjY0IiB5PSItNTcwLjE2IiBmb250LWZhbWlseT0iQ291cmllcixtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMTIuMDAiIGZpbGw9IiMwMDAwMDAiPm9mZj0weDc8L3RleHQ+CjwvZz4KPCEtLSBlMCAtLT4KPGcgaWQ9Im5vZGU0IiBjbGFzcz0ibm9kZSI+Cjx0aXRsZT5lMDwvdGl0bGU+Cjxwb2x5Z29uIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxLjEiIHBvaW50cz0iMjgxLjQ2LC02NjkuOTIgMjQ1LjgyLC02NjkuOTIgMjQ1LjgyLC02NTIuODUgMjgxLjQ2LC02NTIuODUgMjgxLjQ2LC02NjkuOTIiLz4KPHRleHQgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iMjYzLjY0IiB5PSItNjU4LjI2IiBmb250LWZhbWlseT0iVGltZXMsc2VyaWYiIGZvbnQtc2l6ZT0iMTAuMDAiIGZpbGw9IiMwMDAwMDAiPmVudHJ5PC90ZXh0Pgo8L2c+CjwhLS0gbjMgLS0+CjxnIGlkPSJub2RlNSIgY2xhc3M9Im5vZGUiPgo8dGl0bGU+bjM8L3RpdGxlPgo8cG9seWdvbiBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMS4xIiBwb2ludHM9IjI5OC41MywtNDQxLjIgMjI4Ljc1LC00NDEuMiAyMjguNzUsLTQwMi42MiAyOTguNTMsLTQwMi42MiAyOTguNTMsLTQ0MS4yIi8+Cjx0ZXh0IHhtbDpzcGFjZT0icHJlc2VydmUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIHg9IjI2My42NCIgeT0iLTQyNC43NiIgZm9udC1mYW1pbHk9IkNvdXJpZXIsbW9ub3NwYWNlIiBmb250LXNpemU9IjEyLjAwIiBmaWxsPSIjMDAwMDAwIj53aGlsZTwvdGV4dD4KPHRleHQgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iMjYzLjY0IiB5PSItNDEwLjUxIiBmb250LWZhbWlseT0iQ291cmllcixtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMTIuMDAiIGZpbGw9IiMwMDAwMDAiPm9mZj0weGI8L3RleHQ+CjwvZz4KPCEtLSBuNCAtLT4KPGcgaWQ9Im5vZGU2IiBjbGFzcz0ibm9kZSI+Cjx0aXRsZT5uNDwvdGl0bGU+Cjxwb2x5Z29uIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxLjEiIHBvaW50cz0iMTg3LjUzLC0yODEuNTUgMTE3Ljc1LC0yODEuNTUgMTE3Ljc1LC0yNDIuOTcgMTg3LjUzLC0yNDIuOTcgMTg3LjUzLC0yODEuNTUiLz4KPHRleHQgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iMTUyLjY0IiB5PSItMjY1LjExIiBmb250LWZhbWlseT0iQ291cmllcixtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMTIuMDAiIGZpbGw9IiMwMDAwMDAiPmlmPC90ZXh0Pgo8dGV4dCB4bWw6c3BhY2U9InByZXNlcnZlIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiB4PSIxNTIuNjQiIHk9Ii0yNTAuODYiIGZvbnQtZmFtaWx5PSJDb3VyaWVyLG1vbm9zcGFjZSIgZm9udC1zaXplPSIxMi4wMCIgZmlsbD0iIzAwMDAwMCI+b2ZmPTB4ZTwvdGV4dD4KPC9nPgo8IS0tIG41IC0tPgo8ZyBpZD0ibm9kZTciIGNsYXNzPSJub2RlIj4KPHRpdGxlPm41PC90aXRsZT4KPHBvbHlnb24gZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgcG9pbnRzPSI3Ny4yOCwtMTE0Ljc4IDAsLTExNC43OCAwLC03Ni4xOSA3Ny4yOCwtNzYuMTkgNzcuMjgsLTExNC43OCIvPgo8dGV4dCB4bWw6c3BhY2U9InByZXNlcnZlIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiB4PSIzOC42NCIgeT0iLTk4LjMzIiBmb250LWZhbWlseT0iQ291cmllcixtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMTIuMDAiIGZpbGw9IiMwMDAwMDAiPnN0b3JlPC90ZXh0Pgo8dGV4dCB4bWw6c3BhY2U9InByZXNlcnZlIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiB4PSIzOC42NCIgeT0iLTg0LjA4IiBmb250LWZhbWlseT0iQ291cmllcixtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMTIuMDAiIGZpbGw9IiMwMDAwMDAiPm9mZj0weDExPC90ZXh0Pgo8L2c+CjwhLS0gZTEgLS0+CjxnIGlkPSJub2RlOCIgY2xhc3M9Im5vZGUiPgo8dGl0bGU+ZTE8L3RpdGxlPgo8cG9seWdvbiBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMS4xIiBwb2ludHM9Ijc4LjU4LC0xOTAuOTcgNDYuNjksLTE5MC45NyA0Ni42OSwtMTczLjkgNzguNTgsLTE3My45IDc4LjU4LC0xOTAuOTciLz4KPHRleHQgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iNjIuNjQiIHk9Ii0xNzkuMzEiIGZvbnQtZmFtaWx5PSJUaW1lcyxzZXJpZiIgZm9udC1zaXplPSIxMC4wMCIgZmlsbD0iIzAwMDAwMCI+dGhlbjwvdGV4dD4KPC9nPgo8IS0tIG42IC0tPgo8ZyBpZD0ibm9kZTkiIGNsYXNzPSJub2RlIj4KPHRpdGxlPm42PC90aXRsZT4KPHBvbHlnb24gZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgcG9pbnRzPSIxOTAuMjgsLTExNC43OCAxMTMsLTExNC43OCAxMTMsLTc2LjE5IDE5MC4yOCwtNzYuMTkgMTkwLjI4LC0xMTQuNzgiLz4KPHRleHQgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iMTUxLjY0IiB5PSItOTguMzMiIGZvbnQtZmFtaWx5PSJDb3VyaWVyLG1vbm9zcGFjZSIgZm9udC1zaXplPSIxMi4wMCIgZmlsbD0iIzAwMDAwMCI+c3RvcmU8L3RleHQ+Cjx0ZXh0IHhtbDpzcGFjZT0icHJlc2VydmUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIHg9IjE1MS42NCIgeT0iLTg0LjA4IiBmb250LWZhbWlseT0iQ291cmllcixtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMTIuMDAiIGZpbGw9IiMwMDAwMDAiPm9mZj0weDE3PC90ZXh0Pgo8L2c+CjwhLS0gZTIgLS0+CjxnIGlkPSJub2RlMTAiIGNsYXNzPSJub2RlIj4KPHRpdGxlPmUyPC90aXRsZT4KPHBvbHlnb24gZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgcG9pbnRzPSIxNjYuMDgsLTE5MC45NyAxMzcuMTksLTE5MC45NyAxMzcuMTksLTE3My45IDE2Ni4wOCwtMTczLjkgMTY2LjA4LC0xOTAuOTciLz4KPHRleHQgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iMTUxLjY0IiB5PSItMTc5LjMxIiBmb250LWZhbWlseT0iVGltZXMsc2VyaWYiIGZvbnQtc2l6ZT0iMTAuMDAiIGZpbGw9IiMwMDAwMDAiPmVsc2U8L3RleHQ+CjwvZz4KPCEtLSBlMyAtLT4KPGcgaWQ9Im5vZGUxMSIgY2xhc3M9Im5vZGUiPgo8dGl0bGU+ZTM8L3RpdGxlPgo8cG9seWdvbiBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMS4xIiBwb2ludHM9IjE5OS4wOCwtMzUwLjYyIDEzNC4xOSwtMzUwLjYyIDEzNC4xOSwtMzMzLjU1IDE5OS4wOCwtMzMzLjU1IDE5OS4wOCwtMzUwLjYyIi8+Cjx0ZXh0IHhtbDpzcGFjZT0icHJlc2VydmUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIHg9IjE2Ni42NCIgeT0iLTMzOC45NiIgZm9udC1mYW1pbHk9IlRpbWVzLHNlcmlmIiBmb250LXNpemU9IjEwLjAwIiBmaWxsPSIjMDAwMDAwIj53aGlsZSYjNDU7Ym9keTwvdGV4dD4KPC9nPgo8IS0tIG43IC0tPgo8ZyBpZD0ibm9kZTEyIiBjbGFzcz0ibm9kZSI+Cjx0aXRsZT5uNzwvdGl0bGU+Cjxwb2x5Z29uIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxLjEiIHBvaW50cz0iMzAzLjI4LC0xMjEuOSAyMjYsLTEyMS45IDIyNiwtNjkuMDcgMzAzLjI4LC02OS4wNyAzMDMuMjgsLTEyMS45Ii8+Cjx0ZXh0IHhtbDpzcGFjZT0icHJlc2VydmUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIHg9IjI2NC42NCIgeT0iLTEwNS40NiIgZm9udC1mYW1pbHk9IkNvdXJpZXIsbW9ub3NwYWNlIiBmb250LXNpemU9IjEyLjAwIiBmaWxsPSIjMDAwMDAwIj5leHByPC90ZXh0Pgo8dGV4dCB4bWw6c3BhY2U9InByZXNlcnZlIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiB4PSIyNjQuNjQiIHk9Ii05MS4yMSIgZm9udC1mYW1pbHk9IkNvdXJpZXIsbW9ub3NwYWNlIiBmb250LXNpemU9IjEyLjAwIiBmaWxsPSIjMDAwMDAwIj5zdWI8L3RleHQ+Cjx0ZXh0IHhtbDpzcGFjZT0icHJlc2VydmUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIHg9IjI2NC42NCIgeT0iLTc2Ljk2IiBmb250LWZhbWlseT0iQ291cmllcixtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMTIuMDAiIGZpbGw9IiMwMDAwMDAiPm9mZj0weDFiPC90ZXh0Pgo8L2c+CjwhLS0gZTQgLS0+CjxnIGlkPSJub2RlMTMiIGNsYXNzPSJub2RlIj4KPHRpdGxlPmU0PC90aXRsZT4KPHBvbHlnb24gZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgcG9pbnRzPSIyNTcuMjEsLTE5MC45NyAyMjYuMDcsLTE5MC45NyAyMjYuMDcsLTE3My45IDI1Ny4yMSwtMTczLjkgMjU3LjIxLC0xOTAuOTciLz4KPHRleHQgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iMjQxLjY0IiB5PSItMTc5LjMxIiBmb250LWZhbWlseT0iVGltZXMsc2VyaWYiIGZvbnQtc2l6ZT0iMTAuMDAiIGZpbGw9IiMwMDAwMDAiPm5leHQ8L3RleHQ+CjwvZz4KPCEtLSBlNSAtLT4KPGcgaWQ9Im5vZGUxNCIgY2xhc3M9Im5vZGUiPgo8dGl0bGU+ZTU8L3RpdGxlPgo8cG9seWdvbiBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMS4xIiBwb2ludHM9IjMyNy45NiwtMTcuMDcgMjk1LjMyLC0xNy4wNyAyOTUuMzIsMCAzMjcuOTYsMCAzMjcuOTYsLTE3LjA3Ii8+Cjx0ZXh0IHhtbDpzcGFjZT0icHJlc2VydmUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIHg9IjMxMS42NCIgeT0iLTUuNDEiIGZvbnQtZmFtaWx5PSJUaW1lcyxzZXJpZiIgZm9udC1zaXplPSIxMC4wMCIgZmlsbD0iIzAwMDAwMCI+YmFjazwvdGV4dD4KPC9nPgo8IS0tIGU2IC0tPgo8ZyBpZD0ibm9kZTE1IiBjbGFzcz0ibm9kZSI+Cjx0aXRsZT5lNjwvdGl0bGU+Cjxwb2x5Z29uIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxLjEiIHBvaW50cz0iMjc5LjIxLC01MTAuMjcgMjQ4LjA3LC01MTAuMjcgMjQ4LjA3LC00OTMuMiAyNzkuMjEsLTQ5My4yIDI3OS4yMSwtNTEwLjI3Ii8+Cjx0ZXh0IHhtbDpzcGFjZT0icHJlc2VydmUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIHg9IjI2My42NCIgeT0iLTQ5OC42MSIgZm9udC1mYW1pbHk9IlRpbWVzLHNlcmlmIiBmb250LXNpemU9IjEwLjAwIiBmaWxsPSIjMDAwMDAwIj5uZXh0PC90ZXh0Pgo8L2c+CjwhLS0gbjggLS0+CjxnIGlkPSJub2RlMTYiIGNsYXNzPSJub2RlIj4KPHRpdGxlPm44PC90aXRsZT4KPHBvbHlnb24gZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgcG9pbnRzPSIzMDIuMjgsLTI4MS41NSAyMjUsLTI4MS41NSAyMjUsLTI0Mi45NyAzMDIuMjgsLTI0Mi45NyAzMDIuMjgsLTI4MS41NSIvPgo8dGV4dCB4bWw6c3BhY2U9InByZXNlcnZlIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiB4PSIyNjMuNjQiIHk9Ii0yNjUuMTEiIGZvbnQtZmFtaWx5PSJDb3VyaWVyLG1vbm9zcGFjZSIgZm9udC1zaXplPSIxMi4wMCIgZmlsbD0iIzAwMDAwMCI+cmV0dXJuPC90ZXh0Pgo8dGV4dCB4bWw6c3BhY2U9InByZXNlcnZlIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiB4PSIyNjMuNjQiIHk9Ii0yNTAuODYiIGZvbnQtZmFtaWx5PSJDb3VyaWVyLG1vbm9zcGFjZSIgZm9udC1zaXplPSIxMi4wMCIgZmlsbD0iIzAwMDAwMCI+b2ZmPTB4MWY8L3RleHQ+CjwvZz4KPCEtLSBlNyAtLT4KPGcgaWQ9Im5vZGUxNyIgY2xhc3M9Im5vZGUiPgo8dGl0bGU+ZTc8L3RpdGxlPgo8cG9seWdvbiBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMS4xIiBwb2ludHM9IjI3OS4yMSwtMzUwLjYyIDI0OC4wNywtMzUwLjYyIDI0OC4wNywtMzMzLjU1IDI3OS4yMSwtMzMzLjU1IDI3OS4yMSwtMzUwLjYyIi8+Cjx0ZXh0IHhtbDpzcGFjZT0icHJlc2VydmUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIHg9IjI2My42NCIgeT0iLTMzOC45NiIgZm9udC1mYW1pbHk9IlRpbWVzLHNlcmlmIiBmb250LXNpemU9IjEwLjAwIiBmaWxsPSIjMDAwMDAwIj5uZXh0PC90ZXh0Pgo8L2c+CjwhLS0gZTggLS0+CjxnIGlkPSJub2RlMTgiIGNsYXNzPSJub2RlIj4KPHRpdGxlPmU4PC90aXRsZT4KPHBvbHlnb24gZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEuMSIgcG9pbnRzPSIyODEuNDYsLTg0My44MiAyNDUuODIsLTg0My44MiAyNDUuODIsLTgyNi43NSAyODEuNDYsLTgyNi43NSAyODEuNDYsLTg0My44MiIvPgo8dGV4dCB4bWw6c3BhY2U9InByZXNlcnZlIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiB4PSIyNjMuNjQiIHk9Ii04MzIuMTYiIGZvbnQtZmFtaWx5PSJUaW1lcyxzZXJpZiIgZm9udC1zaXplPSIxMC4wMCIgZmlsbD0iIzAwMDAwMCI+ZW50cnk8L3RleHQ+CjwvZz4KPC9nPgo8L3N2Zz4K" x="1060" y="50" width="445" height="1200" preserveAspectRatio="xMidYMid meet"/> 6 + </svg>
+27
fuzz/lf.c
··· 1 + #include "cf.h" 2 + #include "da.h" 3 + #include "dc.h" 4 + #include "js.h" 5 + #include "p.h" 6 + 7 + #include <stdint.h> 8 + #include <stdlib.h> 9 + 10 + int LLVMFuzzerTestOneInput(const uint8_t *data, size_t len) { 11 + tobi_parse_result r; 12 + (void)tobi_parse(data, len, 0, &r); 13 + if (r.root) { 14 + (void)tobi_cf_recover(r.root, &r.diag); 15 + char *dc = tobi_dc_emit(r.root, &r.diag); 16 + char *js = tobi_js_emit(&r); 17 + char *da = tobi_da_emit(data, len, &r.meta); 18 + free(da); 19 + free(js); 20 + free(dc); 21 + } 22 + tobi_parse_result_free(&r); 23 + 24 + (void)tobi_parse(data, len, 1, &r); 25 + tobi_parse_result_free(&r); 26 + return 0; 27 + }
sam/ll.aml

This is a binary file and will not be displayed.

+56 -28
src/cf.c
··· 35 35 tobi_ir_add(parent, note); 36 36 } 37 37 38 + static void add_diag_node(tobi_diag_list *diag, tobi_diag_level level, const tobi_ir *n, const char *msg) { 39 + tobi_diag_add_source(diag, level, n ? n->off : 0, n ? n->source : NULL, 40 + n ? n->input_index : 0, n ? n->has_source : 0, "%s", msg); 41 + } 42 + 38 43 static void walk(tobi_ir *n, tobi_diag_list *diag, unsigned loop_depth) { 39 44 if (!n) { 40 45 return; 41 46 } 42 47 if (n->kind == TOBI_IR_IF && n->child_len < 2) { 43 - tobi_diag_add(diag, TOBI_DIAG_ERROR, n->off, "if node lacks predicate or body"); 48 + add_diag_node(diag, TOBI_DIAG_ERROR, n, "if node lacks predicate or body"); 44 49 } 45 50 if (n->kind == TOBI_IR_WHILE && n->child_len < 2) { 46 - tobi_diag_add(diag, TOBI_DIAG_ERROR, n->off, "while node lacks predicate or body"); 51 + add_diag_node(diag, TOBI_DIAG_ERROR, n, "while node lacks predicate or body"); 47 52 } 48 53 if ((n->kind == TOBI_IR_BREAK || n->kind == TOBI_IR_CONTINUE) && loop_depth == 0) { 49 - tobi_diag_add(diag, TOBI_DIAG_WARN, n->off, "%s outside loop kept as explicit statement", 50 - n->kind == TOBI_IR_BREAK ? "break" : "continue"); 54 + tobi_diag_add_source(diag, TOBI_DIAG_WARN, n->off, n->source, n->input_index, n->has_source, 55 + "%s outside loop kept as explicit statement", 56 + n->kind == TOBI_IR_BREAK ? "break" : "continue"); 51 57 } 52 58 size_t original_children = n->child_len; 53 59 unsigned child_loop_depth = loop_depth + (n->kind == TOBI_IR_WHILE ? 1u : 0u); ··· 81 87 } 82 88 } 83 89 84 - static size_t dot_stmt(tobi_sb *sb, const tobi_ir *n, size_t *next); 90 + typedef struct { 91 + size_t stmt_next; 92 + size_t edge_next; 93 + } dot_ctx; 85 94 86 - static void dot_edge(tobi_sb *sb, size_t from, size_t to, const char *label) { 87 - tobi_sb_printf(sb, " n%zu -> n%zu", from, to); 95 + static size_t dot_stmt(tobi_sb *sb, const tobi_ir *n, dot_ctx *ctx); 96 + 97 + static void dot_edge(tobi_sb *sb, dot_ctx *ctx, size_t from, size_t to, const char *label) { 88 98 if (label && label[0]) { 89 - tobi_sb_add(sb, " [label=\""); 99 + size_t id = ctx->edge_next++; 100 + tobi_sb_printf(sb, " e%zu [shape=box,style=\"solid\",", id); 101 + tobi_sb_add(sb, "color=\"#000000\",fontcolor=\"#000000\","); 102 + tobi_sb_add(sb, "fontname=\"Times-Roman\",fontsize=10,margin=\"0.06,0.03\","); 103 + tobi_sb_add(sb, "height=0,width=0,label=\""); 90 104 dot_escape(sb, label); 91 - tobi_sb_add(sb, "\"]"); 105 + tobi_sb_add(sb, "\"];\n"); 106 + tobi_sb_printf(sb, " n%zu -> e%zu [arrowhead=none, weight=2];\n", from, id); 107 + tobi_sb_printf(sb, " e%zu -> n%zu [weight=2];\n", id, to); 108 + return; 92 109 } 93 - tobi_sb_add(sb, ";\n"); 110 + tobi_sb_printf(sb, " n%zu -> n%zu;\n", from, to); 94 111 } 95 112 96 - static void dot_block(tobi_sb *sb, const tobi_ir *b, size_t owner, const char *edge_label, size_t *next) { 113 + static int dot_block(tobi_sb *sb, const tobi_ir *b, size_t owner, const char *edge_label, dot_ctx *ctx, size_t *last) { 97 114 if (!b) { 98 - return; 115 + return 0; 99 116 } 100 117 size_t prev = owner; 118 + int emitted = 0; 101 119 for (size_t i = 0; i < b->child_len; i++) { 102 - size_t id = dot_stmt(sb, b->child[i], next); 103 - dot_edge(sb, prev, id, i == 0 ? edge_label : "next"); 120 + size_t id = dot_stmt(sb, b->child[i], ctx); 121 + dot_edge(sb, ctx, prev, id, i == 0 ? edge_label : "next"); 104 122 prev = id; 123 + emitted = 1; 105 124 } 125 + if (last) { 126 + *last = prev; 127 + } 128 + return emitted; 106 129 } 107 130 108 - static size_t dot_stmt(tobi_sb *sb, const tobi_ir *n, size_t *next) { 109 - size_t id = (*next)++; 131 + static size_t dot_stmt(tobi_sb *sb, const tobi_ir *n, dot_ctx *ctx) { 132 + size_t id = ctx->stmt_next++; 110 133 tobi_sb_printf(sb, " n%zu [label=\"%s", id, tobi_ir_kind_name(n->kind)); 111 134 if (n->name && n->name[0]) { 112 135 tobi_sb_add(sb, "\\n"); ··· 121 144 } 122 145 tobi_sb_add(sb, "\"];\n"); 123 146 if (n->kind == TOBI_IR_METHOD && n->child_len > 0) { 124 - dot_block(sb, n->child[0], id, "entry", next); 147 + (void)dot_block(sb, n->child[0], id, "entry", ctx, NULL); 125 148 } else if (n->kind == TOBI_IR_SCOPE || n->kind == TOBI_IR_DEVICE || n->kind == TOBI_IR_PROCESSOR) { 126 149 if (n->child_len > 0) { 127 - dot_block(sb, n->child[0], id, "body", next); 150 + (void)dot_block(sb, n->child[0], id, "body", ctx, NULL); 128 151 } 129 152 } else if (n->kind == TOBI_IR_IF) { 130 153 if (n->child_len > 1) { 131 - dot_block(sb, n->child[1], id, "then", next); 154 + (void)dot_block(sb, n->child[1], id, "then", ctx, NULL); 132 155 } 133 156 if (n->child_len > 2) { 134 - dot_block(sb, n->child[2], id, "else", next); 157 + (void)dot_block(sb, n->child[2], id, "else", ctx, NULL); 135 158 } 136 159 } else if (n->kind == TOBI_IR_WHILE) { 137 160 if (n->child_len > 1) { 138 - dot_block(sb, n->child[1], id, "while-body", next); 139 - tobi_sb_printf(sb, " n%zu -> n%zu [label=\"back\"];\n", (*next) - 1u, id); 161 + size_t last = id; 162 + if (dot_block(sb, n->child[1], id, "while-body", ctx, &last)) { 163 + dot_edge(sb, ctx, last, id, "back"); 164 + } 140 165 } 141 166 } 142 167 return id; ··· 146 171 tobi_sb sb; 147 172 tobi_sb_init(&sb); 148 173 tobi_sb_add(&sb, "digraph tobi_cfg {\n"); 149 - tobi_sb_add(&sb, " graph [rankdir=TB];\n"); 150 - tobi_sb_add(&sb, " node [shape=box,fontname=\"monospace\"];\n"); 174 + tobi_sb_add(&sb, " graph [rankdir=TB,bgcolor=\"#ffffff\",pad=0.18,nodesep=0.5,ranksep=0.72,"); 175 + tobi_sb_add(&sb, "splines=ortho,outputorder=edgesfirst];\n"); 176 + tobi_sb_add(&sb, " node [shape=box,style=\"solid\",color=\"#000000\",fontcolor=\"#000000\","); 177 + tobi_sb_add(&sb, "fontname=\"Courier\",fontsize=12,margin=\"0.12,0.07\",penwidth=1.1];\n"); 178 + tobi_sb_add(&sb, " edge [color=\"#000000\",fontcolor=\"#000000\",arrowsize=0.75,penwidth=1.1];\n"); 151 179 if (root) { 152 - size_t next = 0; 153 - size_t root_id = dot_stmt(&sb, root, &next); 180 + dot_ctx ctx = {0, 0}; 181 + size_t root_id = dot_stmt(&sb, root, &ctx); 154 182 size_t prev = root_id; 155 183 for (size_t i = 0; i < root->child_len; i++) { 156 - size_t id = dot_stmt(&sb, root->child[i], &next); 157 - dot_edge(&sb, prev, id, i == 0 ? "entry" : "next"); 184 + size_t id = dot_stmt(&sb, root->child[i], &ctx); 185 + dot_edge(&sb, &ctx, prev, id, i == 0 ? "entry" : "next"); 158 186 prev = id; 159 187 } 160 188 }
+19 -8
src/dc.c
··· 1 1 #include "dc.h" 2 2 3 3 #include "mem.h" 4 + #include "op.h" 4 5 #include "str.h" 5 6 6 7 #include <stdio.h> ··· 111 112 case TOBI_IR_EXPR: { 112 113 const char *op = binop(n->name); 113 114 int prec = op_prec(n->name); 115 + const tobi_op_expr *info = tobi_op_expr_find_name(n->name); 116 + size_t arg_len = n->child_len; 117 + if (info && info->target_count <= arg_len) { 118 + arg_len -= info->target_count; 119 + } 114 120 if (op && n->child_len >= 2 && prec > 0) { 115 121 int paren = prec < parent_prec; 116 122 if (paren) { ··· 125 131 return; 126 132 } 127 133 op = unop(n->name); 128 - if (op && n->child_len == 1 && prec > 0) { 134 + if (op && arg_len == 1 && prec > 0) { 129 135 int paren = prec < parent_prec; 130 136 if (paren) { 131 137 tobi_sb_ch(sb, '('); ··· 138 144 return; 139 145 } 140 146 tobi_sb_printf(sb, "%s(", n->name ? n->name : "op"); 141 - for (size_t i = 0; i < n->child_len; i++) { 147 + for (size_t i = 0; i < arg_len; i++) { 142 148 if (i) { 143 149 tobi_sb_add(sb, ", "); 144 150 } ··· 186 192 if (!n || n->kind != TOBI_IR_EXPR || !n->name) { 187 193 return 0; 188 194 } 189 - if (strcmp(n->name, "div") == 0) { 190 - return n->child_len >= 4; 191 - } 192 - return binop(n->name) && n->child_len >= 3; 195 + const tobi_op_expr *info = tobi_op_expr_find_name(n->name); 196 + return info && info->target_count > 0 && 197 + n->child_len >= (size_t)info->arg_count + info->target_count; 193 198 } 194 199 195 200 static void expr_target_stmt(tobi_sb *sb, const tobi_ir *n) { 196 - if (strcmp(n->name, "div") == 0 && n->child_len >= 4) { 201 + const tobi_op_expr *info = tobi_op_expr_find_name(n->name); 202 + if (!info || info->target_count == 0 || 203 + n->child_len < (size_t)info->arg_count + info->target_count) { 204 + expr(sb, n); 205 + return; 206 + } 207 + if (info->target_count == 2 && strcmp(n->name, "div") == 0 && n->child_len >= 4) { 197 208 expr(sb, n->child[2]); 198 209 tobi_sb_add(sb, ", "); 199 210 expr(sb, n->child[3]); ··· 204 215 tobi_sb_ch(sb, ')'); 205 216 return; 206 217 } 207 - expr(sb, n->child[2]); 218 + expr(sb, n->child[info->arg_count]); 208 219 tobi_sb_add(sb, " = "); 209 220 expr(sb, n); 210 221 }
+48 -18
src/dg.c
··· 6 6 #include <stdio.h> 7 7 #include <stdlib.h> 8 8 9 + static void diag_addv(tobi_diag_list *dl, tobi_diag_level level, size_t off, 10 + const char *source, size_t input_index, int has_source, 11 + const char *fmt, va_list ap) { 12 + if (dl->len == dl->cap) { 13 + dl->cap = tobi_xgrow_cap(dl->cap, dl->len + 1u, 8u); 14 + dl->items = tobi_xrealloc(dl->items, tobi_xmul_size(dl->cap, sizeof(dl->items[0]))); 15 + } 16 + va_list cp; 17 + va_copy(cp, ap); 18 + int n = vsnprintf(NULL, 0, fmt, cp); 19 + va_end(cp); 20 + if (n < 0) { 21 + n = 0; 22 + } 23 + char *msg = tobi_xmalloc((size_t)n + 1); 24 + (void)vsnprintf(msg, (size_t)n + 1, fmt, ap); 25 + dl->items[dl->len].level = level; 26 + dl->items[dl->len].off = off; 27 + dl->items[dl->len].source = has_source && source ? tobi_xstrdup(source) : NULL; 28 + dl->items[dl->len].input_index = input_index; 29 + dl->items[dl->len].has_source = has_source && source != NULL; 30 + dl->items[dl->len].msg = msg; 31 + dl->len++; 32 + } 33 + 9 34 void tobi_diag_init(tobi_diag_list *dl) { 10 35 dl->items = NULL; 11 36 dl->len = 0; ··· 14 39 15 40 void tobi_diag_free(tobi_diag_list *dl) { 16 41 for (size_t i = 0; i < dl->len; i++) { 42 + free(dl->items[i].source); 17 43 free(dl->items[i].msg); 18 44 } 19 45 free(dl->items); ··· 23 49 } 24 50 25 51 void tobi_diag_add(tobi_diag_list *dl, tobi_diag_level level, size_t off, const char *fmt, ...) { 26 - if (dl->len == dl->cap) { 27 - dl->cap = dl->cap ? dl->cap * 2 : 8; 28 - dl->items = tobi_xrealloc(dl->items, dl->cap * sizeof(dl->items[0])); 29 - } 30 52 va_list ap; 31 53 va_start(ap, fmt); 32 - va_list cp; 33 - va_copy(cp, ap); 34 - int n = vsnprintf(NULL, 0, fmt, cp); 35 - va_end(cp); 36 - if (n < 0) { 37 - va_end(ap); 38 - n = 0; 54 + diag_addv(dl, level, off, NULL, 0, 0, fmt, ap); 55 + va_end(ap); 56 + } 57 + 58 + void tobi_diag_add_source(tobi_diag_list *dl, tobi_diag_level level, size_t off, 59 + const char *source, size_t input_index, int has_source, 60 + const char *fmt, ...) { 61 + va_list ap; 62 + va_start(ap, fmt); 63 + diag_addv(dl, level, off, source, input_index, has_source, fmt, ap); 64 + va_end(ap); 65 + } 66 + 67 + void tobi_diag_set_source(tobi_diag_list *dl, const char *source, size_t input_index) { 68 + if (!dl || !source) { 69 + return; 70 + } 71 + for (size_t i = 0; i < dl->len; i++) { 72 + free(dl->items[i].source); 73 + dl->items[i].source = tobi_xstrdup(source); 74 + dl->items[i].input_index = input_index; 75 + dl->items[i].has_source = 1; 39 76 } 40 - char *msg = tobi_xmalloc((size_t)n + 1); 41 - (void)vsnprintf(msg, (size_t)n + 1, fmt, ap); 42 - va_end(ap); 43 - dl->items[dl->len].level = level; 44 - dl->items[dl->len].off = off; 45 - dl->items[dl->len].msg = msg; 46 - dl->len++; 47 77 } 48 78 49 79 int tobi_diag_has_error(const tobi_diag_list *dl) {
+9
src/dg.h
··· 11 11 typedef struct tobi_diag { 12 12 tobi_diag_level level; 13 13 size_t off; 14 + char *source; 15 + size_t input_index; 16 + int has_source; 14 17 char *msg; 15 18 } tobi_diag; 16 19 ··· 25 28 void tobi_diag_free(tobi_diag_list *dl); 26 29 27 30 void tobi_diag_add(tobi_diag_list *dl, tobi_diag_level level, size_t off, const char *fmt, ...); 31 + 32 + void tobi_diag_add_source(tobi_diag_list *dl, tobi_diag_level level, size_t off, 33 + const char *source, size_t input_index, int has_source, 34 + const char *fmt, ...); 35 + 36 + void tobi_diag_set_source(tobi_diag_list *dl, const char *source, size_t input_index); 28 37 29 38 int tobi_diag_has_error(const tobi_diag_list *dl); 30 39
+29 -4
src/ir.c
··· 27 27 free(node->scope); 28 28 free(node->str); 29 29 free(node->target); 30 + free(node->source); 30 31 free(node); 31 32 } 32 33 ··· 35 36 return; 36 37 } 37 38 if (parent->child_len == parent->child_cap) { 38 - parent->child_cap = parent->child_cap ? parent->child_cap * 2 : 4; 39 - parent->child = tobi_xrealloc(parent->child, parent->child_cap * sizeof(parent->child[0])); 39 + parent->child_cap = tobi_xgrow_cap(parent->child_cap, parent->child_len + 1u, 4u); 40 + parent->child = tobi_xrealloc(parent->child, 41 + tobi_xmul_size(parent->child_cap, sizeof(parent->child[0]))); 42 + } 43 + if (parent->has_source && !child->has_source) { 44 + tobi_ir_set_source_recursive(child, parent->source, parent->input_index); 40 45 } 41 46 child->parent_id = parent->id; 42 47 child->parent_index = parent->child_len; ··· 64 69 cur.node->parent_index = cur.parent_index; 65 70 for (size_t i = cur.node->child_len; i > 0; i--) { 66 71 if (len == cap) { 67 - cap *= 2u; 68 - stack = tobi_xrealloc(stack, cap * sizeof(stack[0])); 72 + cap = tobi_xgrow_cap(cap, len + 1u, 64u); 73 + stack = tobi_xrealloc(stack, tobi_xmul_size(cap, sizeof(stack[0]))); 69 74 } 70 75 stack[len++] = (frame){cur.node->child[i - 1u], cur.node->id, i - 1u}; 71 76 } ··· 96 101 void tobi_ir_set_target(tobi_ir *node, const char *s) { 97 102 free(node->target); 98 103 node->target = tobi_xstrdup(s ? s : ""); 104 + } 105 + 106 + void tobi_ir_set_source(tobi_ir *node, const char *s, size_t input_index) { 107 + if (!node || !s) { 108 + return; 109 + } 110 + free(node->source); 111 + node->source = tobi_xstrdup(s); 112 + node->input_index = input_index; 113 + node->has_source = 1; 114 + } 115 + 116 + void tobi_ir_set_source_recursive(tobi_ir *node, const char *s, size_t input_index) { 117 + if (!node || !s) { 118 + return; 119 + } 120 + tobi_ir_set_source(node, s, input_index); 121 + for (size_t i = 0; i < node->child_len; i++) { 122 + tobi_ir_set_source_recursive(node->child[i], s, input_index); 123 + } 99 124 } 100 125 101 126 const char *tobi_ir_kind_name(tobi_ir_kind kind) {
+7
src/ir.h
··· 48 48 char *scope; 49 49 char *str; 50 50 char *target; 51 + char *source; 51 52 uint64_t value; 52 53 uint64_t bit_off; 53 54 uint64_t bit_len; ··· 61 62 struct tobi_ir **child; 62 63 size_t child_len; 63 64 size_t child_cap; 65 + size_t input_index; 66 + int has_source; 64 67 } tobi_ir; 65 68 66 69 tobi_ir *tobi_ir_new(tobi_ir_kind kind, size_t off, size_t len); ··· 80 83 void tobi_ir_set_str(tobi_ir *node, const char *s); 81 84 82 85 void tobi_ir_set_target(tobi_ir *node, const char *s); 86 + 87 + void tobi_ir_set_source(tobi_ir *node, const char *s, size_t input_index); 88 + 89 + void tobi_ir_set_source_recursive(tobi_ir *node, const char *s, size_t input_index); 83 90 84 91 const char *tobi_ir_kind_name(tobi_ir_kind kind); 85 92
+57 -22
src/js.c
··· 53 53 tobi_sb_add(sb, "\"target\":"); 54 54 tobi_json_string(sb, n->target); 55 55 } 56 + if (n->has_source && n->source) { 57 + comma(sb, &first); 58 + tobi_sb_add(sb, "\"source\":"); 59 + tobi_json_string(sb, n->source); 60 + comma(sb, &first); 61 + tobi_sb_printf(sb, "\"input_index\":%zu", n->input_index); 62 + } 56 63 if (n->kind == TOBI_IR_INTEGER || n->kind == TOBI_IR_PACKAGE || n->kind == TOBI_IR_FIELD || 57 64 n->kind == TOBI_IR_FIELD_ELEM || n->kind == TOBI_IR_CALL || n->kind == TOBI_IR_OPREGION || 58 65 n->kind == TOBI_IR_RESOURCE) { ··· 115 122 tobi_sb_add(sb, ",\"target\":"); 116 123 tobi_json_string(sb, e->target); 117 124 } 125 + if (e->has_source && e->source) { 126 + tobi_sb_add(sb, ",\"source\":"); 127 + tobi_json_string(sb, e->source); 128 + tobi_sb_printf(sb, ",\"input_index\":%zu", e->input_index); 129 + } 118 130 tobi_sb_ch(sb, '}'); 119 131 } 120 132 tobi_sb_ch(sb, ']'); 121 133 } 122 134 135 + static void meta_obj(tobi_sb *sb, const tobi_input_meta *meta) { 136 + tobi_sb_ch(sb, '{'); 137 + tobi_sb_printf(sb, "\"is_table\":%s,", meta->is_table ? "true" : "false"); 138 + tobi_sb_add(sb, "\"source\":"); 139 + tobi_json_string(sb, meta->source); 140 + tobi_sb_add(sb, ",\"signature\":"); 141 + tobi_json_string(sb, meta->signature); 142 + tobi_sb_printf(sb, ",\"table_length\":%zu,\"aml_offset\":%zu,\"aml_length\":%zu," 143 + "\"revision\":%u,\"checksum_byte\":%u,\"checksum_sum\":%u," 144 + "\"checksum_valid\":%s,\"header_hash\":%u,\"aml_hash\":%u,", 145 + meta->table_len, meta->aml_off, meta->aml_len, 146 + (unsigned)meta->revision, (unsigned)meta->checksum_byte, 147 + (unsigned)meta->checksum_sum, meta->checksum_valid ? "true" : "false", 148 + meta->header_hash, meta->aml_hash); 149 + tobi_sb_add(sb, "\"oem_id\":"); 150 + tobi_json_string(sb, meta->oem_id); 151 + tobi_sb_add(sb, ",\"oem_table_id\":"); 152 + tobi_json_string(sb, meta->oem_table_id); 153 + tobi_sb_printf(sb, ",\"oem_revision\":%u,", meta->oem_revision); 154 + tobi_sb_add(sb, "\"creator_id\":"); 155 + tobi_json_string(sb, meta->creator_id); 156 + tobi_sb_printf(sb, ",\"creator_revision\":%u}", meta->creator_revision); 157 + } 158 + 159 + static void inputs_emit(tobi_sb *sb, const tobi_parse_result *res) { 160 + tobi_sb_add(sb, "\"inputs\":["); 161 + for (size_t i = 0; i < res->input_len; i++) { 162 + if (i) { 163 + tobi_sb_ch(sb, ','); 164 + } 165 + meta_obj(sb, &res->inputs[i]); 166 + } 167 + tobi_sb_ch(sb, ']'); 168 + } 169 + 123 170 char *tobi_js_emit(const tobi_parse_result *res) { 124 171 tobi_sb sb; 125 172 tobi_sb_init(&sb); 126 - tobi_sb_add(&sb, "{\"meta\":{"); 127 - tobi_sb_printf(&sb, "\"is_table\":%s,", res->meta.is_table ? "true" : "false"); 128 - tobi_sb_add(&sb, "\"source\":"); 129 - tobi_json_string(&sb, res->meta.source); 130 - tobi_sb_add(&sb, ","); 131 - tobi_sb_add(&sb, "\"signature\":"); 132 - tobi_json_string(&sb, res->meta.signature); 133 - tobi_sb_printf(&sb, ",\"table_length\":%zu,\"aml_offset\":%zu,\"aml_length\":%zu," 134 - "\"revision\":%u,\"checksum_byte\":%u,\"checksum_sum\":%u," 135 - "\"checksum_valid\":%s,\"header_hash\":%u,\"aml_hash\":%u,", 136 - res->meta.table_len, res->meta.aml_off, res->meta.aml_len, 137 - (unsigned)res->meta.revision, (unsigned)res->meta.checksum_byte, 138 - (unsigned)res->meta.checksum_sum, res->meta.checksum_valid ? "true" : "false", 139 - res->meta.header_hash, res->meta.aml_hash); 140 - tobi_sb_add(&sb, "\"oem_id\":"); 141 - tobi_json_string(&sb, res->meta.oem_id); 142 - tobi_sb_add(&sb, ",\"oem_table_id\":"); 143 - tobi_json_string(&sb, res->meta.oem_table_id); 144 - tobi_sb_printf(&sb, ",\"oem_revision\":%u,", res->meta.oem_revision); 145 - tobi_sb_add(&sb, "\"creator_id\":"); 146 - tobi_json_string(&sb, res->meta.creator_id); 147 - tobi_sb_printf(&sb, ",\"creator_revision\":%u},", res->meta.creator_revision); 173 + tobi_sb_add(&sb, "{\"meta\":"); 174 + meta_obj(&sb, &res->meta); 175 + tobi_sb_ch(&sb, ','); 176 + inputs_emit(&sb, res); 177 + tobi_sb_ch(&sb, ','); 148 178 tobi_sb_add(&sb, "\"diagnostics\":["); 149 179 for (size_t i = 0; i < res->diag.len; i++) { 150 180 if (i) { ··· 154 184 tobi_json_string(&sb, tobi_diag_level_name(res->diag.items[i].level)); 155 185 tobi_sb_printf(&sb, ",\"offset\":%zu,\"message\":", res->diag.items[i].off); 156 186 tobi_json_string(&sb, res->diag.items[i].msg); 187 + if (res->diag.items[i].has_source && res->diag.items[i].source) { 188 + tobi_sb_add(&sb, ",\"source\":"); 189 + tobi_json_string(&sb, res->diag.items[i].source); 190 + tobi_sb_printf(&sb, ",\"input_index\":%zu", res->diag.items[i].input_index); 191 + } 157 192 tobi_sb_ch(&sb, '}'); 158 193 } 159 194 tobi_sb_add(&sb, "],");
+40 -11
src/main.c
··· 9 9 #include <stdio.h> 10 10 #include <stdlib.h> 11 11 #include <string.h> 12 + #include <unistd.h> 12 13 13 14 #define TOBI_VERSION "0.1.0" 14 15 ··· 33 34 return 0; 34 35 } 35 36 if (fseek(fp, 0, SEEK_END) != 0) { 37 + fprintf(stderr, "tobi: %s: seek failed: %s\n", path, strerror(errno)); 36 38 fclose(fp); 37 39 return 0; 38 40 } 39 41 long n = ftell(fp); 40 42 if (n < 0) { 43 + fprintf(stderr, "tobi: %s: size failed: %s\n", path, strerror(errno)); 41 44 fclose(fp); 42 45 return 0; 43 46 } ··· 45 48 *data = tobi_xmalloc((size_t)n ? (size_t)n : 1); 46 49 *len = (size_t)n; 47 50 if (*len && fread(*data, 1, *len, fp) != *len) { 51 + fprintf(stderr, "tobi: %s: read failed%s%s\n", path, ferror(fp) ? ": " : "", 52 + ferror(fp) ? strerror(errno) : ""); 48 53 fclose(fp); 49 54 free(*data); 50 55 *data = NULL; 51 56 *len = 0; 52 57 return 0; 53 58 } 54 - fclose(fp); 59 + if (fclose(fp) != 0) { 60 + fprintf(stderr, "tobi: %s: close failed: %s\n", path, strerror(errno)); 61 + free(*data); 62 + *data = NULL; 63 + *len = 0; 64 + return 0; 65 + } 55 66 return 1; 56 67 } 57 68 58 69 static void print_diags(const tobi_diag_list *dl, int plain) { 59 - (void)plain; 70 + int colour = !plain && isatty(fileno(stderr)); 60 71 for (size_t i = 0; i < dl->len; i++) { 61 - fprintf(stderr, "0x%zx: %s: %s\n", dl->items[i].off, 62 - tobi_diag_level_name(dl->items[i].level), dl->items[i].msg); 72 + const char *lvl = tobi_diag_level_name(dl->items[i].level); 73 + const char *c = dl->items[i].level == TOBI_DIAG_ERROR ? "\033[31m" : "\033[33m"; 74 + if (dl->items[i].has_source && dl->items[i].source) { 75 + fprintf(stderr, "%s:", dl->items[i].source); 76 + } 77 + if (colour) { 78 + fprintf(stderr, "0x%zx: %s%s\033[0m: %s\n", dl->items[i].off, c, lvl, dl->items[i].msg); 79 + } else { 80 + fprintf(stderr, "0x%zx: %s: %s\n", dl->items[i].off, lvl, dl->items[i].msg); 81 + } 63 82 } 64 83 } 65 84 ··· 68 87 return; 69 88 } 70 89 (void)snprintf(meta->source, sizeof(meta->source), "%s", path); 90 + } 91 + 92 + static void apply_source(tobi_parse_result *res, const char *path, size_t input_index) { 93 + set_source(&res->meta, path); 94 + if (input_index < res->input_len) { 95 + set_source(&res->inputs[input_index], path); 96 + } 97 + tobi_diag_set_source(&res->diag, path, input_index); 98 + tobi_ns_set_source(&res->ns, path, input_index); 99 + if (res->root) { 100 + tobi_ir_set_source_recursive(res->root, path, input_index); 101 + } 71 102 } 72 103 73 104 static void print_raw_meta(const char *input, const tobi_parse_result *res) { ··· 118 149 return 2; 119 150 } else { 120 151 if (file_len == file_cap) { 121 - file_cap = file_cap ? file_cap * 2u : 4u; 122 - files = tobi_xrealloc(files, file_cap * sizeof(files[0])); 152 + file_cap = tobi_xgrow_cap(file_cap, file_len + 1u, 4u); 153 + files = tobi_xrealloc(files, tobi_xmul_size(file_cap, sizeof(files[0]))); 123 154 } 124 155 files[file_len++] = argv[i]; 125 156 } ··· 152 183 for (size_t i = 0; i < file_len; i++) { 153 184 tobi_parse_result one; 154 185 int ok = tobi_parse(data[i], lens[i], strict, &one); 155 - set_source(&one.meta, files[i]); 186 + apply_source(&one, files[i], 0); 156 187 if (mode_raw) { 157 188 print_raw_meta(files[i], &one); 158 189 } else { ··· 181 212 int ok = 0; 182 213 if (file_len == 1) { 183 214 ok = tobi_parse(data[0], lens[0], strict, &res); 184 - set_source(&res.meta, files[0]); 215 + apply_source(&res, files[0], 0); 185 216 } else { 186 217 tobi_parse_input *inputs = tobi_xcalloc(file_len, sizeof(inputs[0])); 187 218 for (size_t i = 0; i < file_len; i++) { ··· 204 235 return 1; 205 236 } 206 237 (void)tobi_cf_recover(res.root, &res.diag); 207 - if (mode_raw) { 208 - print_raw_meta(files[0], &res); 209 - } else if (mode_json) { 238 + if (mode_json) { 210 239 char *s = tobi_js_emit(&res); 211 240 fputs(s, stdout); 212 241 free(s);
+26
src/mem.c
··· 43 43 return p; 44 44 } 45 45 46 + size_t tobi_xadd_size(size_t a, size_t b) { 47 + if (a > SIZE_MAX - b) { 48 + oom(); 49 + } 50 + return a + b; 51 + } 52 + 53 + size_t tobi_xmul_size(size_t a, size_t b) { 54 + if (b != 0 && a > SIZE_MAX / b) { 55 + oom(); 56 + } 57 + return a * b; 58 + } 59 + 60 + size_t tobi_xgrow_cap(size_t current, size_t required, size_t initial) { 61 + size_t cap = current ? current : (initial ? initial : 1u); 62 + while (cap < required) { 63 + if (cap > SIZE_MAX / 2u) { 64 + cap = required; 65 + break; 66 + } 67 + cap *= 2u; 68 + } 69 + return cap; 70 + } 71 + 46 72 char *tobi_xstrdup(const char *s) { 47 73 size_t n = strlen(s); 48 74 char *out = tobi_xmalloc(n + 1);
+6
src/mem.h
··· 9 9 10 10 void *tobi_xrealloc(void *ptr, size_t n); 11 11 12 + size_t tobi_xadd_size(size_t a, size_t b); 13 + 14 + size_t tobi_xmul_size(size_t a, size_t b); 15 + 16 + size_t tobi_xgrow_cap(size_t current, size_t required, size_t initial); 17 + 12 18 char *tobi_xstrdup(const char *s); 13 19 14 20 char *tobi_xstrndup(const char *s, size_t n);
+22 -2
src/nm.c
··· 181 181 free(e->name); 182 182 free(e->owner); 183 183 free(e->target); 184 + free(e->source); 184 185 } 185 186 186 187 void tobi_ns_free(tobi_ns *ns) { ··· 392 393 return old; 393 394 } 394 395 if (ns->len == ns->cap) { 395 - ns->cap = ns->cap ? ns->cap * 2 : 16; 396 - ns->items = tobi_xrealloc(ns->items, ns->cap * sizeof(ns->items[0])); 396 + ns->cap = tobi_xgrow_cap(ns->cap, ns->len + 1u, 16u); 397 + ns->items = tobi_xrealloc(ns->items, tobi_xmul_size(ns->cap, sizeof(ns->items[0]))); 397 398 } 398 399 tobi_ns_ent *e = &ns->items[ns->len++]; 399 400 memset(e, 0, sizeof(*e)); ··· 408 409 e->external = external; 409 410 return e; 410 411 } 412 + 413 + void tobi_ns_ent_set_source(tobi_ns_ent *e, const char *source, size_t input_index) { 414 + if (!e || !source) { 415 + return; 416 + } 417 + free(e->source); 418 + e->source = tobi_xstrdup(source); 419 + e->input_index = input_index; 420 + e->has_source = 1; 421 + } 422 + 423 + void tobi_ns_set_source(tobi_ns *ns, const char *source, size_t input_index) { 424 + if (!ns || !source) { 425 + return; 426 + } 427 + for (size_t i = 0; i < ns->len; i++) { 428 + tobi_ns_ent_set_source(&ns->items[i], source, input_index); 429 + } 430 + }
+7
src/nm.h
··· 26 26 char *name; 27 27 char *owner; 28 28 char *target; 29 + char *source; 29 30 tobi_ns_kind kind; 30 31 size_t off; 31 32 uint64_t region_offset; ··· 34 35 unsigned flags; 35 36 int has_region_range; 36 37 int external; 38 + size_t input_index; 39 + int has_source; 37 40 } tobi_ns_ent; 38 41 39 42 typedef struct tobi_ns { ··· 73 76 tobi_ns_ent *tobi_ns_add(tobi_ns *ns, tobi_ns_kind kind, const char *path, const char *owner, 74 77 size_t off, unsigned args, unsigned flags, int external, 75 78 const char *target, int *duplicate); 79 + 80 + void tobi_ns_ent_set_source(tobi_ns_ent *e, const char *source, size_t input_index); 81 + 82 + void tobi_ns_set_source(tobi_ns *ns, const char *source, size_t input_index); 76 83 77 84 #endif
+84
src/op.c
··· 1 1 #include "op.h" 2 2 3 3 #include <stddef.h> 4 + #include <string.h> 4 5 5 6 static const tobi_op ops[] = { 6 7 {0x00, "ZeroOp", TOBI_OP_SIMPLE, 0}, ··· 220 221 {0x5b88, "NameString SignatureString OemIdString OemTableIdString"}, 221 222 }; 222 223 224 + static const tobi_op_expr exprs[] = { 225 + {0x71, "ref_of", 1, 0, 0}, 226 + {0x72, "add", 2, 1, 0}, 227 + {0x73, "concat", 2, 1, 0}, 228 + {0x74, "sub", 2, 1, 0}, 229 + {0x75, "inc", 1, 0, 0}, 230 + {0x76, "dec", 1, 0, 0}, 231 + {0x77, "mul", 2, 1, 0}, 232 + {0x78, "div", 2, 2, 0}, 233 + {0x79, "shl", 2, 1, 0}, 234 + {0x7a, "shr", 2, 1, 0}, 235 + {0x7b, "and", 2, 1, 0}, 236 + {0x7c, "nand", 2, 1, 0}, 237 + {0x7d, "or", 2, 1, 0}, 238 + {0x7e, "nor", 2, 1, 0}, 239 + {0x7f, "xor", 2, 1, 0}, 240 + {0x80, "not", 1, 1, 0}, 241 + {0x81, "find_set_left_bit", 1, 1, 0}, 242 + {0x82, "find_set_right_bit", 1, 1, 0}, 243 + {0x83, "deref_of", 1, 0, 0}, 244 + {0x84, "concat_res", 2, 1, 0}, 245 + {0x85, "mod", 2, 1, 0}, 246 + {0x86, "notify", 2, 0, 0}, 247 + {0x87, "size_of", 1, 0, 0}, 248 + {0x88, "index", 2, 1, 0}, 249 + {0x89, "match", 6, 0, 0}, 250 + {0x8a, "create_dword_field", 2, 0, 1}, 251 + {0x8b, "create_word_field", 2, 0, 1}, 252 + {0x8c, "create_byte_field", 2, 0, 1}, 253 + {0x8d, "create_bit_field", 2, 0, 1}, 254 + {0x8e, "object_type", 1, 0, 0}, 255 + {0x8f, "create_qword_field", 2, 0, 1}, 256 + {0x90, "land", 2, 0, 0}, 257 + {0x91, "lor", 2, 0, 0}, 258 + {0x92, "lnot", 1, 0, 0}, 259 + {0x93, "eq", 2, 0, 0}, 260 + {0x94, "gt", 2, 0, 0}, 261 + {0x95, "lt", 2, 0, 0}, 262 + {0x96, "to_buffer", 1, 1, 0}, 263 + {0x97, "to_decimal_string", 1, 1, 0}, 264 + {0x98, "to_hex_string", 1, 1, 0}, 265 + {0x99, "to_integer", 1, 1, 0}, 266 + {0x9c, "to_string", 2, 1, 0}, 267 + {0x9d, "copy_object", 1, 1, 0}, 268 + {0x9e, "mid", 3, 1, 0}, 269 + {0x5b12, "cond_ref_of", 1, 1, 0}, 270 + {0x5b13, "create_field", 3, 0, 1}, 271 + {0x5b1f, "load_table", 5, 1, 0}, 272 + {0x5b20, "load", 1, 1, 0}, 273 + {0x5b21, "stall", 1, 0, 0}, 274 + {0x5b22, "sleep", 1, 0, 0}, 275 + {0x5b23, "acquire", 2, 0, 0}, 276 + {0x5b24, "signal", 1, 0, 0}, 277 + {0x5b25, "wait", 2, 0, 0}, 278 + {0x5b26, "reset", 1, 0, 0}, 279 + {0x5b27, "release", 1, 0, 0}, 280 + {0x5b28, "from_bcd", 1, 1, 0}, 281 + {0x5b29, "to_bcd", 1, 1, 0}, 282 + {0x5b2a, "unload", 1, 0, 0}, 283 + {0x5b32, "fatal", 3, 0, 0}, 284 + }; 285 + 223 286 const tobi_op *tobi_op_find(uint8_t op) { 224 287 for (size_t i = 0; i < sizeof(ops) / sizeof(ops[0]); i++) { 225 288 if (ops[i].code == op) { ··· 254 317 } 255 318 return NULL; 256 319 } 320 + 321 + const tobi_op_expr *tobi_op_expr_find(uint16_t code) { 322 + for (size_t i = 0; i < sizeof(exprs) / sizeof(exprs[0]); i++) { 323 + if (exprs[i].code == code) { 324 + return &exprs[i]; 325 + } 326 + } 327 + return NULL; 328 + } 329 + 330 + const tobi_op_expr *tobi_op_expr_find_name(const char *name) { 331 + if (!name) { 332 + return NULL; 333 + } 334 + for (size_t i = 0; i < sizeof(exprs) / sizeof(exprs[0]); i++) { 335 + if (strcmp(exprs[i].ir_name, name) == 0) { 336 + return &exprs[i]; 337 + } 338 + } 339 + return NULL; 340 + }
+12
src/op.h
··· 24 24 const char *operands; 25 25 } tobi_op_desc; 26 26 27 + typedef struct tobi_op_expr { 28 + uint16_t code; 29 + const char *ir_name; 30 + unsigned arg_count; 31 + unsigned target_count; 32 + unsigned name_count; 33 + } tobi_op_expr; 34 + 27 35 const tobi_op *tobi_op_find(uint8_t op); 28 36 29 37 const tobi_op *tobi_op_find_ext(uint8_t ext); ··· 31 39 const tobi_op *tobi_op_find_code(uint16_t code); 32 40 33 41 const char *tobi_op_operands(uint16_t code); 42 + 43 + const tobi_op_expr *tobi_op_expr_find(uint16_t code); 44 + 45 + const tobi_op_expr *tobi_op_expr_find_name(const char *name); 34 46 35 47 #endif
+118 -135
src/p.c
··· 19 19 int strict; 20 20 char *scope; 21 21 tobi_ns *ns; 22 + const char *source; 22 23 size_t terms; 24 + size_t input_index; 25 + int has_source; 23 26 } parser; 24 27 25 28 static void diag(parser *p, tobi_diag_level lvl, size_t off, const char *fmt, ...) { ··· 35 38 char *msg = tobi_xmalloc((size_t)n + 1); 36 39 (void)vsnprintf(msg, (size_t)n + 1, fmt, ap); 37 40 va_end(ap); 38 - tobi_diag_add(p->diag, lvl, off, "%s", msg); 41 + tobi_diag_add_source(p->diag, lvl, off, p->source, p->input_index, p->has_source, "%s", msg); 39 42 free(msg); 40 43 } 41 44 ··· 112 115 return; 113 116 } 114 117 int duplicate = 0; 115 - (void)tobi_ns_add(p->ns, kind, path, p->scope, off, args, flags, external, target, &duplicate); 118 + tobi_ns_ent *ent = tobi_ns_add(p->ns, kind, path, p->scope, off, args, flags, external, target, &duplicate); 119 + if (ent && p->has_source && (!duplicate || !ent->has_source)) { 120 + tobi_ns_ent_set_source(ent, p->source, p->input_index); 121 + } 116 122 if (duplicate) { 117 123 diag(p, p->strict ? TOBI_DIAG_ERROR : TOBI_DIAG_WARN, off, 118 124 "duplicate namespace object %s at %s", tobi_ns_kind_name(kind), path); ··· 144 150 return sum; 145 151 } 146 152 147 - static int detect_input(const uint8_t *data, size_t len, int strict, tobi_diag_list *diag_list, tobi_input_meta *meta) { 153 + static int detect_input(const uint8_t *data, size_t len, int strict, tobi_diag_list *diag_list, 154 + tobi_input_meta *meta, const char *source, size_t input_index, 155 + int has_source) { 148 156 memset(meta, 0, sizeof(*meta)); 149 157 meta->aml_off = 0; 150 158 meta->aml_len = len; 151 159 memcpy(meta->signature, "RAW", 4); 152 160 meta->signature[4] = '\0'; 153 - memcpy(meta->source, "raw", 4); 161 + (void)snprintf(meta->source, sizeof(meta->source), "%s", has_source && source ? source : "raw"); 154 162 if (len >= 4 && is_table_sig(data)) { 155 163 memcpy(meta->signature, data, 4); 156 164 meta->signature[4] = '\0'; 157 165 meta->is_table = 1; 158 166 if (len < 36) { 159 - tobi_diag_add(diag_list, TOBI_DIAG_ERROR, 0, "ACPI table shorter than 36-byte header"); 167 + tobi_diag_add_source(diag_list, TOBI_DIAG_ERROR, 0, source, input_index, has_source, 168 + "ACPI table shorter than 36-byte header"); 160 169 return !strict; 161 170 } 162 171 uint32_t table_len = le32_at(data + 4); 163 172 meta->table_len = table_len; 164 173 if (table_len < 36 || table_len > len) { 165 - tobi_diag_add(diag_list, TOBI_DIAG_ERROR, 4, "invalid ACPI table length %u for file size %zu", table_len, len); 174 + tobi_diag_add_source(diag_list, TOBI_DIAG_ERROR, 4, source, input_index, has_source, 175 + "invalid ACPI table length %u for file size %zu", table_len, len); 166 176 return !strict; 167 177 } 168 178 meta->revision = data[8]; ··· 177 187 meta->checksum_sum = sum; 178 188 meta->checksum_valid = sum == 0; 179 189 if (sum != 0) { 180 - tobi_diag_add(diag_list, strict ? TOBI_DIAG_ERROR : TOBI_DIAG_WARN, 9, 181 - "ACPI table checksum mismatch: byte sum is 0x%02x", sum); 190 + tobi_diag_add_source(diag_list, strict ? TOBI_DIAG_ERROR : TOBI_DIAG_WARN, 9, 191 + source, input_index, has_source, 192 + "ACPI table checksum mismatch: byte sum is 0x%02x", sum); 182 193 if (strict) { 183 194 return 0; 184 195 } ··· 695 706 rd->pos = body_start + body_len; 696 707 return unknown_node(p, op_start, raw, whole_len, kind_name); 697 708 } 698 - (void)tobi_rd_skip(&body, prefix_bytes); 709 + if (!tobi_rd_skip(&body, prefix_bytes)) { 710 + diag(p, TOBI_DIAG_ERROR, tobi_rd_off(&body), "truncated %s metadata", kind_name); 711 + rd->pos = body_start + body_len; 712 + free(name); 713 + return unknown_node(p, op_start, raw, whole_len, kind_name); 714 + } 699 715 char *path = tobi_nm_resolve(p->scope, name); 700 716 tobi_ir *n = tobi_ir_new(kind, op_start, whole_len); 701 717 tobi_ir_set_name(n, name); ··· 740 756 uint8_t proc_id = 0; 741 757 uint32_t pblk = 0; 742 758 uint8_t pblk_len = 0; 743 - (void)tobi_rd_u8(&body, &proc_id); 744 - (void)tobi_rd_u32(&body, &pblk); 745 - (void)tobi_rd_u8(&body, &pblk_len); 759 + if (!tobi_rd_u8(&body, &proc_id) || !tobi_rd_u32(&body, &pblk) || 760 + !tobi_rd_u8(&body, &pblk_len)) { 761 + diag(p, TOBI_DIAG_ERROR, tobi_rd_off(&body), "truncated ProcessorOp metadata"); 762 + rd->pos = body_start + body_len; 763 + free(name); 764 + return unknown_node(p, op_start, raw, whole_len, "bad ProcessorOp metadata"); 765 + } 746 766 char *path = tobi_nm_resolve(p->scope, name); 747 767 tobi_ir *n = tobi_ir_new(TOBI_IR_PROCESSOR, op_start, whole_len); 748 768 tobi_ir_set_name(n, name); ··· 881 901 ns_declare_path(p, ext == 0x01 ? TOBI_NS_MUTEX : TOBI_NS_EVENT, path, op_start, 0, 0, 0, NULL); 882 902 if (ext == 0x01) { 883 903 uint8_t sync = 0; 884 - (void)tobi_rd_u8(rd, &sync); 904 + if (!tobi_rd_u8(rd, &sync)) { 905 + free(path); 906 + free(name); 907 + diag(p, TOBI_DIAG_ERROR, tobi_rd_off(rd), "truncated MutexOp sync flags"); 908 + return unknown_node(p, op_start, raw, tobi_rd_off(rd) - op_start, "bad MutexOp sync flags"); 909 + } 885 910 n->value = sync; 886 911 } 887 912 n->len = tobi_rd_off(rd) - op_start; ··· 949 974 return n; 950 975 } 951 976 952 - static int expr_opcode_arity(uint8_t op, int *target_count, const char **name) { 953 - *target_count = 0; 954 - switch (op) { 955 - case 0x71: *name = "ref_of"; return 1; 956 - case 0x72: *target_count = 1; *name = "add"; return 2; 957 - case 0x73: *target_count = 1; *name = "concat"; return 2; 958 - case 0x74: *target_count = 1; *name = "sub"; return 2; 959 - case 0x75: *name = "inc"; return 1; 960 - case 0x76: *name = "dec"; return 1; 961 - case 0x77: *target_count = 1; *name = "mul"; return 2; 962 - case 0x78: *target_count = 2; *name = "div"; return 2; 963 - case 0x79: *target_count = 1; *name = "shl"; return 2; 964 - case 0x7a: *target_count = 1; *name = "shr"; return 2; 965 - case 0x7b: *target_count = 1; *name = "and"; return 2; 966 - case 0x7c: *target_count = 1; *name = "nand"; return 2; 967 - case 0x7d: *target_count = 1; *name = "or"; return 2; 968 - case 0x7e: *target_count = 1; *name = "nor"; return 2; 969 - case 0x7f: *target_count = 1; *name = "xor"; return 2; 970 - case 0x80: *target_count = 1; *name = "not"; return 1; 971 - case 0x81: *target_count = 1; *name = "find_set_left_bit"; return 1; 972 - case 0x82: *target_count = 1; *name = "find_set_right_bit"; return 1; 973 - case 0x83: *name = "deref_of"; return 1; 974 - case 0x84: *target_count = 1; *name = "concat_res"; return 2; 975 - case 0x85: *target_count = 1; *name = "mod"; return 2; 976 - case 0x86: *name = "notify"; return 2; 977 - case 0x87: *name = "size_of"; return 1; 978 - case 0x88: *target_count = 1; *name = "index"; return 2; 979 - case 0x89: *name = "match"; return 6; 980 - case 0x8e: *name = "object_type"; return 1; 981 - case 0x90: *name = "land"; return 2; 982 - case 0x91: *name = "lor"; return 2; 983 - case 0x92: *name = "lnot"; return 1; 984 - case 0x93: *name = "eq"; return 2; 985 - case 0x94: *name = "gt"; return 2; 986 - case 0x95: *name = "lt"; return 2; 987 - case 0x96: *target_count = 1; *name = "to_buffer"; return 1; 988 - case 0x97: *target_count = 1; *name = "to_decimal_string"; return 1; 989 - case 0x98: *target_count = 1; *name = "to_hex_string"; return 1; 990 - case 0x99: *target_count = 1; *name = "to_integer"; return 1; 991 - case 0x9c: *target_count = 1; *name = "to_string"; return 2; 992 - case 0x9d: *target_count = 1; *name = "copy_object"; return 1; 993 - case 0x9e: *target_count = 1; *name = "mid"; return 3; 994 - default: return -1; 995 - } 996 - } 997 - 998 - static const char *create_field_name(uint8_t op) { 999 - switch (op) { 1000 - case 0x8a: return "create_dword_field"; 1001 - case 0x8b: return "create_word_field"; 1002 - case 0x8c: return "create_byte_field"; 1003 - case 0x8d: return "create_bit_field"; 1004 - case 0x8f: return "create_qword_field"; 1005 - default: return "create_field"; 1006 - } 1007 - } 1008 - 1009 977 static tobi_ir *parse_string(parser *p, tobi_rd *rd, size_t op_start) { 1010 978 size_t s = rd->pos; 1011 979 while (rd->pos < rd->len && rd->data[rd->pos] != 0) { ··· 1395 1363 n->len = tobi_rd_off(rd) - off; 1396 1364 return n; 1397 1365 } 1398 - if ((op >= 0x8a && op <= 0x8d) || op == 0x8f) { 1366 + const tobi_op_expr *info = tobi_op_expr_find(op); 1367 + if (info) { 1399 1368 tobi_ir *n = tobi_ir_new(TOBI_IR_EXPR, off, 0); 1400 1369 n->raw_op = op; 1401 - tobi_ir_set_name(n, create_field_name(op)); 1402 - add_expr_children(p, rd, depth, n, 2); 1403 - tobi_ir *dst = ref_from_name(p, rd, off, "bad create-field namestring"); 1404 - if (dst) { 1405 - if (dst->path && dst->path[0] == '\\') { 1406 - ns_declare_path(p, TOBI_NS_FIELD, dst->path, dst->off, 0, op, 0, NULL); 1407 - } 1408 - tobi_ir_add(n, dst); 1409 - } 1410 - n->len = tobi_rd_off(rd) - off; 1411 - return n; 1412 - } 1413 - int target_count = 0; 1414 - const char *ename = NULL; 1415 - int arity = expr_opcode_arity(op, &target_count, &ename); 1416 - if (arity >= 0) { 1417 - tobi_ir *n = tobi_ir_new(TOBI_IR_EXPR, off, 0); 1418 - tobi_ir_set_name(n, ename); 1419 - n->raw_op = op; 1420 - for (int i = 0; i < arity; i++) { 1421 - tobi_ir *arg = parse_expr(p, rd, depth + 1); 1422 - if (arg) { 1423 - tobi_ir_add(n, arg); 1424 - } 1425 - } 1426 - for (int i = 0; i < target_count; i++) { 1427 - tobi_ir *target = parse_expr(p, rd, depth + 1); 1428 - if (target) { 1429 - tobi_ir_add(n, target); 1370 + tobi_ir_set_name(n, info->ir_name); 1371 + add_expr_children(p, rd, depth, n, info->arg_count); 1372 + add_expr_children(p, rd, depth, n, info->target_count); 1373 + for (unsigned i = 0; i < info->name_count; i++) { 1374 + tobi_ir *dst = ref_from_name(p, rd, off, "bad expression namestring"); 1375 + if (dst) { 1376 + if (dst->path && dst->path[0] == '\\' && strncmp(info->ir_name, "create_", 7) == 0) { 1377 + ns_declare_path(p, TOBI_NS_FIELD, dst->path, dst->off, 0, op, 0, NULL); 1378 + } 1379 + tobi_ir_add(n, dst); 1430 1380 } 1431 1381 } 1432 1382 n->len = tobi_rd_off(rd) - off; ··· 1654 1604 } 1655 1605 } 1656 1606 1657 - static void sem_diag(parser *p, size_t off, const char *fmt, ...) { 1607 + static void sem_diag_source(parser *p, size_t off, const char *source, size_t input_index, 1608 + int has_source, const char *fmt, ...) { 1658 1609 va_list ap; 1659 1610 va_start(ap, fmt); 1660 1611 va_list cp; ··· 1667 1618 char *msg = tobi_xmalloc((size_t)n + 1); 1668 1619 (void)vsnprintf(msg, (size_t)n + 1, fmt, ap); 1669 1620 va_end(ap); 1670 - diag(p, p->strict ? TOBI_DIAG_ERROR : TOBI_DIAG_WARN, off, "%s", msg); 1621 + tobi_diag_add_source(p->diag, p->strict ? TOBI_DIAG_ERROR : TOBI_DIAG_WARN, off, 1622 + source, input_index, has_source, "%s", msg); 1623 + free(msg); 1624 + } 1625 + 1626 + static void sem_diag_node(parser *p, const tobi_ir *n, const char *fmt, ...) { 1627 + va_list ap; 1628 + va_start(ap, fmt); 1629 + va_list cp; 1630 + va_copy(cp, ap); 1631 + int sz = vsnprintf(NULL, 0, fmt, cp); 1632 + va_end(cp); 1633 + if (sz < 0) { 1634 + sz = 0; 1635 + } 1636 + char *msg = tobi_xmalloc((size_t)sz + 1); 1637 + (void)vsnprintf(msg, (size_t)sz + 1, fmt, ap); 1638 + va_end(ap); 1639 + sem_diag_source(p, n ? n->off : 0, n ? n->source : p->source, 1640 + n ? n->input_index : p->input_index, 1641 + n ? n->has_source : p->has_source, "%s", msg); 1671 1642 free(msg); 1672 1643 } 1673 1644 ··· 1712 1683 if (n->kind == TOBI_IR_CALL && n->path && n->path[0] == '\\') { 1713 1684 const tobi_ns_ent *ent = tobi_ns_find_const(p->ns, n->path); 1714 1685 if (!ent || ent->kind != TOBI_NS_METHOD) { 1715 - sem_diag(p, n->off, "unresolved AML method call %s", n->path); 1686 + sem_diag_node(p, n, "unresolved AML method call %s", n->path); 1716 1687 } else if (n->child_len != (size_t)ent->args) { 1717 - sem_diag(p, n->off, "AML method call %s has %zu args but namespace declares %u", 1718 - n->path, n->child_len, ent->args); 1688 + sem_diag_node(p, n, "AML method call %s has %zu args but namespace declares %u", 1689 + n->path, n->child_len, ent->args); 1719 1690 } 1720 1691 } else if (n->kind == TOBI_IR_REF && n->path && n->path[0] == '\\' && semantic_ref_is_value(parent)) { 1721 1692 const tobi_ns_ent *ent = tobi_ns_find_const(p->ns, n->path); 1722 1693 if (!ent) { 1723 - sem_diag(p, n->off, "unresolved AML namespace reference %s", n->path); 1694 + sem_diag_node(p, n, "unresolved AML namespace reference %s", n->path); 1724 1695 } 1725 1696 } 1726 1697 for (size_t i = 0; i < n->child_len; i++) { ··· 1762 1733 } else if ((st->locals & (uint16_t)(1u << idx)) == 0) { 1763 1734 st->local_reads |= (uint16_t)(1u << idx); 1764 1735 st->warnings++; 1765 - sem_diag(st->p, n->off, "Local%u may be read before being initialised in method %s", 1766 - idx, st->method->path ? st->method->path : st->method->name); 1736 + sem_diag_node(st->p, n, "Local%u may be read before being initialised in method %s", 1737 + idx, st->method->path ? st->method->path : st->method->name); 1767 1738 tobi_ir_set_str(n, "method_symbol=local read=uninitialised"); 1768 1739 } else { 1769 1740 st->local_reads |= (uint16_t)(1u << idx); ··· 1777 1748 } 1778 1749 if (idx >= st->method->method_args) { 1779 1750 st->warnings++; 1780 - sem_diag(st->p, n->off, "Arg%u %s exceeds declared argument count %u in method %s", 1781 - idx, write_target ? "write" : "read", 1782 - st->method->method_args, st->method->path ? st->method->path : st->method->name); 1751 + sem_diag_node(st->p, n, "Arg%u %s exceeds declared argument count %u in method %s", 1752 + idx, write_target ? "write" : "read", 1753 + st->method->method_args, st->method->path ? st->method->path : st->method->name); 1783 1754 tobi_ir_set_str(n, write_target ? "method_symbol=arg write=out_of_range" : 1784 1755 "method_symbol=arg read=out_of_range"); 1785 1756 } else if (write_target) { ··· 1825 1796 } 1826 1797 if (n->kind == TOBI_IR_EXPR && n->name) { 1827 1798 size_t target_start = n->child_len; 1828 - if (strcmp(n->name, "div") == 0 && n->child_len >= 4) { 1829 - target_start = 2; 1830 - } else if ((strcmp(n->name, "add") == 0 || strcmp(n->name, "concat") == 0 || 1831 - strcmp(n->name, "sub") == 0 || strcmp(n->name, "mul") == 0 || 1832 - strcmp(n->name, "shl") == 0 || strcmp(n->name, "shr") == 0 || 1833 - strcmp(n->name, "and") == 0 || strcmp(n->name, "nand") == 0 || 1834 - strcmp(n->name, "or") == 0 || strcmp(n->name, "nor") == 0 || 1835 - strcmp(n->name, "xor") == 0 || strcmp(n->name, "not") == 0 || 1836 - strcmp(n->name, "find_set_left_bit") == 0 || 1837 - strcmp(n->name, "find_set_right_bit") == 0 || 1838 - strcmp(n->name, "concat_res") == 0 || strcmp(n->name, "mod") == 0 || 1839 - strcmp(n->name, "index") == 0 || strcmp(n->name, "to_buffer") == 0 || 1840 - strcmp(n->name, "to_decimal_string") == 0 || 1841 - strcmp(n->name, "to_hex_string") == 0 || strcmp(n->name, "to_integer") == 0 || 1842 - strcmp(n->name, "to_string") == 0 || strcmp(n->name, "copy_object") == 0 || 1843 - strcmp(n->name, "mid") == 0) && n->child_len >= 3) { 1844 - target_start = n->child_len - 1u; 1799 + const tobi_op_expr *info = tobi_op_expr_find_name(n->name); 1800 + if (info && info->target_count > 0 && 1801 + n->child_len >= (size_t)info->arg_count + info->target_count) { 1802 + target_start = info->arg_count; 1845 1803 } 1846 1804 for (size_t i = 0; i < target_start; i++) { 1847 1805 method_symbols_node(st, n->child[i]); ··· 1923 1881 for (size_t i = 0; i < p->ns->len; i++) { 1924 1882 const tobi_ns_ent *e = &p->ns->items[i]; 1925 1883 if (e->external && e->kind == TOBI_NS_METHOD) { 1926 - sem_diag(p, e->off, "external AML method %s remains unresolved", e->path); 1884 + sem_diag_source(p, e->off, e->source, e->input_index, e->has_source, 1885 + "external AML method %s remains unresolved", e->path); 1927 1886 } 1928 1887 if (e->kind == TOBI_NS_ALIAS) { 1929 1888 if (e->target && !tobi_ns_find_const(p->ns, e->target)) { 1930 - sem_diag(p, e->off, "AML alias %s targets unresolved object %s", e->path, e->target); 1889 + sem_diag_source(p, e->off, e->source, e->input_index, e->has_source, 1890 + "AML alias %s targets unresolved object %s", e->path, e->target); 1931 1891 } else if (semantic_alias_cycle_from(p->ns, e)) { 1932 - sem_diag(p, e->off, "AML alias cycle includes %s", e->path); 1892 + sem_diag_source(p, e->off, e->source, e->input_index, e->has_source, 1893 + "AML alias cycle includes %s", e->path); 1933 1894 } 1934 1895 } 1935 1896 } ··· 1941 1902 memset(out, 0, sizeof(*out)); 1942 1903 tobi_diag_init(&out->diag); 1943 1904 tobi_ns_init(&out->ns); 1944 - if (!detect_input(data, len, strict, &out->diag, &out->meta)) { 1905 + if (!detect_input(data, len, strict, &out->diag, &out->meta, "raw", 0, 1)) { 1945 1906 return 0; 1946 1907 } 1908 + out->inputs = tobi_xcalloc(1u, sizeof(out->inputs[0])); 1909 + out->inputs[0] = out->meta; 1910 + out->input_len = 1; 1947 1911 out->root = tobi_ir_new(TOBI_IR_ROOT, out->meta.aml_off, out->meta.aml_len); 1948 1912 tobi_ir_set_scope(out->root, "\\"); 1913 + tobi_ir_set_source(out->root, out->meta.source, 0); 1949 1914 parser p; 1950 1915 memset(&p, 0, sizeof(p)); 1951 1916 p.diag = &out->diag; 1952 1917 p.strict = strict; 1953 1918 p.ns = &out->ns; 1954 1919 p.scope = tobi_xstrdup("\\"); 1920 + p.source = out->meta.source; 1921 + p.input_index = 0; 1922 + p.has_source = 1; 1955 1923 tobi_rd rd; 1956 1924 if (out->meta.aml_off <= len && out->meta.aml_len <= len - out->meta.aml_off) { 1957 1925 tobi_rd scan; ··· 1975 1943 memcpy(out->meta.source, "multi", 6); 1976 1944 out->root = tobi_ir_new(TOBI_IR_ROOT, 0, 0); 1977 1945 tobi_ir_set_scope(out->root, "\\"); 1946 + tobi_ir_set_source(out->root, "multi", 0); 1978 1947 if (!inputs || count == 0) { 1979 1948 tobi_diag_add(&out->diag, TOBI_DIAG_ERROR, 0, "no AML inputs"); 1980 1949 return 0; 1981 1950 } 1982 1951 tobi_input_meta *metas = tobi_xcalloc(count, sizeof(metas[0])); 1952 + out->inputs = metas; 1953 + out->input_len = count; 1983 1954 int ok = 1; 1984 1955 parser p; 1985 1956 memset(&p, 0, sizeof(p)); ··· 1988 1959 p.ns = &out->ns; 1989 1960 p.scope = tobi_xstrdup("\\"); 1990 1961 for (size_t i = 0; i < count; i++) { 1991 - if (!detect_input(inputs[i].data, inputs[i].len, strict, &out->diag, &metas[i])) { 1962 + p.source = inputs[i].name; 1963 + p.input_index = i; 1964 + p.has_source = inputs[i].name != NULL; 1965 + if (!detect_input(inputs[i].data, inputs[i].len, strict, &out->diag, &metas[i], 1966 + inputs[i].name, i, inputs[i].name != NULL)) { 1992 1967 ok = 0; 1993 1968 continue; 1994 1969 } 1995 - out->meta.aml_len += metas[i].aml_len; 1970 + out->meta.aml_len = tobi_xadd_size(out->meta.aml_len, metas[i].aml_len); 1996 1971 if (metas[i].aml_off <= inputs[i].len && metas[i].aml_len <= inputs[i].len - metas[i].aml_off) { 1997 1972 tobi_rd scan; 1998 1973 tobi_rd_init(&scan, inputs[i].data + metas[i].aml_off, metas[i].aml_len, metas[i].aml_off); ··· 2004 1979 if (metas[i].aml_off > inputs[i].len || metas[i].aml_len > inputs[i].len - metas[i].aml_off) { 2005 1980 continue; 2006 1981 } 1982 + p.source = inputs[i].name; 1983 + p.input_index = i; 1984 + p.has_source = inputs[i].name != NULL; 2007 1985 tobi_ir *file = tobi_ir_new(TOBI_IR_BLOCK, metas[i].aml_off, metas[i].aml_len); 2008 1986 tobi_ir_set_name(file, inputs[i].name ? inputs[i].name : "<input>"); 2009 1987 tobi_ir_set_scope(file, "\\"); 1988 + if (inputs[i].name) { 1989 + tobi_ir_set_source(file, inputs[i].name, i); 1990 + } 2010 1991 tobi_rd rd; 2011 1992 tobi_rd_init(&rd, inputs[i].data + metas[i].aml_off, metas[i].aml_len, metas[i].aml_off); 2012 1993 (void)parse_term_list(&p, &rd, file, 0); ··· 2016 1997 semantic_validate(&p, out->root); 2017 1998 tobi_ir_assign_ids(out->root); 2018 1999 parser_free(&p); 2019 - free(metas); 2020 2000 return ok && !(strict && tobi_diag_has_error(&out->diag)); 2021 2001 } 2022 2002 2023 2003 void tobi_parse_result_free(tobi_parse_result *res) { 2024 2004 tobi_ir_free(res->root); 2025 2005 res->root = NULL; 2006 + free(res->inputs); 2007 + res->inputs = NULL; 2008 + res->input_len = 0; 2026 2009 tobi_ns_free(&res->ns); 2027 2010 tobi_diag_free(&res->diag); 2028 2011 }
+2
src/p.h
··· 33 33 tobi_diag_list diag; 34 34 tobi_ns ns; 35 35 tobi_input_meta meta; 36 + tobi_input_meta *inputs; 37 + size_t input_len; 36 38 } tobi_parse_result; 37 39 38 40 typedef struct tobi_parse_input {
+2 -5
src/str.c
··· 8 8 #include <string.h> 9 9 10 10 static void need(tobi_sb *sb, size_t add) { 11 - size_t req = sb->len + add + 1; 11 + size_t req = tobi_xadd_size(tobi_xadd_size(sb->len, add), 1u); 12 12 if (req <= sb->cap) { 13 13 return; 14 14 } 15 - size_t cap = sb->cap ? sb->cap : 64; 16 - while (cap < req) { 17 - cap *= 2; 18 - } 15 + size_t cap = tobi_xgrow_cap(sb->cap, req, 64u); 19 16 sb->buf = tobi_xrealloc(sb->buf, cap); 20 17 sb->cap = cap; 21 18 }
+6
test/t_bad.c
··· 31 31 T_CHECK(parse_has_error(bad_table, sizeof(bad_table))); 32 32 uint8_t bad_sum[40] = {'S','S','D','T',40,0,0,0}; 33 33 T_CHECK(strict_fails(bad_sum, sizeof(bad_sum))); 34 + uint8_t trunc_mutex[] = {0x5b,0x01,'M','T','X','0'}; 35 + T_CHECK(parse_has_error(trunc_mutex, sizeof(trunc_mutex))); 36 + uint8_t trunc_processor[] = {0x5b,0x83,0x05,'P','R','C','0'}; 37 + T_CHECK(parse_has_error(trunc_processor, sizeof(trunc_processor))); 38 + uint8_t trunc_power[] = {0x5b,0x84,0x05,'P','W','R','0'}; 39 + T_CHECK(parse_has_error(trunc_power, sizeof(trunc_power))); 34 40 uint8_t rnd[128]; 35 41 for (size_t i = 0; i < sizeof(rnd); i++) { 36 42 rnd[i] = (uint8_t)(i * 37u + 11u);
+26
test/t_cf.c
··· 25 25 T_CHECK(while_n->child[1]->child[0]->kind == TOBI_IR_IF); 26 26 tobi_parse_result_free(&r); 27 27 28 + uint8_t ll[] = { 29 + 0x14,0x24,'L','L','0','0',0x01, 30 + 0x70,0x00,0x60, 31 + 0x70,0x0a,0x03,0x61, 32 + 0xa2,0x14,0x61, 33 + 0xa0,0x06,0x68,0x72,0x60,0x01,0x60, 34 + 0xa1,0x06,0x72,0x60,0x0a,0x02,0x60, 35 + 0x74,0x61,0x01,0x61, 36 + 0xa4,0x60 37 + }; 38 + T_CHECK(tobi_parse(ll, sizeof(ll), 0, &r)); 39 + T_CHECK(tobi_cf_recover(r.root, &r.diag)); 40 + tobi_ir *ll_method = r.root->child[0]; 41 + T_CHECK(ll_method->kind == TOBI_IR_METHOD); 42 + T_CHECK(strcmp(ll_method->name, "LL00") == 0); 43 + tobi_ir *ll_block = ll_method->child[0]; 44 + T_CHECK(ll_block->child_len == 4); 45 + T_CHECK(ll_block->child[2]->kind == TOBI_IR_WHILE); 46 + T_CHECK(ll_block->child[2]->child[1]->child[0]->kind == TOBI_IR_IF); 47 + dot = tobi_cf_dot(r.root); 48 + T_STR(dot, "LL00"); 49 + T_STR(dot, "while"); 50 + T_STR(dot, "back"); 51 + free(dot); 52 + tobi_parse_result_free(&r); 53 + 28 54 uint8_t amb[] = {0xa0,0x03,0x01,0xa1}; 29 55 T_CHECK(tobi_parse(amb, sizeof(amb), 0, &r)); 30 56 T_CHECK(r.root->child_len == 1);
+24
test/t_cli.c
··· 77 77 T_CHECK(st == 0); 78 78 T_STR(s, "\"ir\""); 79 79 T_STR(s, "\"kind\":\"method\""); 80 + T_STR(s, path); 81 + T_CHECK(strstr(s, "\"source\":\"raw\"") == NULL); 80 82 free(s); 81 83 snprintf(args, sizeof(args), "--dot %s", path); 82 84 s = run_capture(args, &st); ··· 84 86 T_STR(s, "digraph tobi_cfg"); 85 87 T_STR(s, "method"); 86 88 free(s); 89 + { 90 + unsigned char user_file[] = { 91 + 0x14,0x24,'L','L','0','0',0x01, 92 + 0x70,0x00,0x60, 93 + 0x70,0x0a,0x03,0x61, 94 + 0xa2,0x14,0x61, 95 + 0xa0,0x06,0x68,0x72,0x60,0x01,0x60, 96 + 0xa1,0x06,0x72,0x60,0x0a,0x02,0x60, 97 + 0x74,0x61,0x01,0x61, 98 + 0xa4,0x60 99 + }; 100 + const char *user_path = "/tmp/tobi_cli_user_dot.aml"; 101 + T_CHECK(write_blob(user_path, user_file, sizeof(user_file))); 102 + snprintf(args, sizeof(args), "--dot %s", user_path); 103 + s = run_capture(args, &st); 104 + T_CHECK(st == 0); 105 + T_STR(s, "digraph tobi_cfg"); 106 + T_STR(s, "LL00"); 107 + T_STR(s, "while"); 108 + T_STR(s, "back"); 109 + free(s); 110 + } 87 111 snprintf(args, sizeof(args), "--raw %s", path); 88 112 s = run_capture(args, &st); 89 113 T_CHECK(st == 0);
+13
test/t_dc.c
··· 36 36 T_CHECK(strstr(s, "Arg0 + (Arg1 * 0x2)") == NULL); 37 37 free(s); 38 38 tobi_parse_result_free(&r); 39 + uint8_t targets[] = { 40 + 0x80,0x00,0x60, 41 + 0x99,0x0d,'4','2',0x00,0x61, 42 + 0x9e,0x68,0x0a,0x01,0x0a,0x02,0x62 43 + }; 44 + T_CHECK(tobi_parse(targets, sizeof(targets), 0, &r)); 45 + s = tobi_dc_emit(r.root, &r.diag); 46 + T_STR(s, "Local0 = ~0x0;"); 47 + T_STR(s, "Local1 = to_integer(\"42\");"); 48 + T_STR(s, "Local2 = mid(Arg0, 0x1, 0x2);"); 49 + T_CHECK(strstr(s, "not(0x0, Local0)") == NULL); 50 + free(s); 51 + tobi_parse_result_free(&r); 39 52 return 0; 40 53 }
+18
test/t_js.c
··· 10 10 char *s = tobi_js_emit(&r); 11 11 T_STR(s, "\"kind\":\"root\""); 12 12 T_STR(s, "\"source\":\"raw\""); 13 + T_STR(s, "\"inputs\":["); 13 14 T_STR(s, "\"checksum_sum\":0"); 14 15 T_STR(s, "\"checksum_valid\":false"); 15 16 T_STR(s, "\"header_hash\":0"); ··· 20 21 T_STR(s, "\"kind\":\"name\""); 21 22 T_STR(s, "a\\\"b\\\\c"); 22 23 T_STR(s, "\"raw_opcode\":254"); 24 + T_CHECK(strstr(s, ",}") == NULL); 25 + T_CHECK(strstr(s, ",]") == NULL); 26 + free(s); 27 + tobi_parse_result_free(&r); 28 + 29 + uint8_t bad[] = {0x14,0x20,'M','T'}; 30 + tobi_parse_input inputs[] = { 31 + {data, sizeof(data), "good.aml"}, 32 + {bad, sizeof(bad), "bad.aml"} 33 + }; 34 + T_CHECK(tobi_parse_multi(inputs, 2, 0, &r)); 35 + s = tobi_js_emit(&r); 36 + T_STR(s, "\"source\":\"good.aml\""); 37 + T_STR(s, "\"source\":\"bad.aml\""); 38 + T_STR(s, "\"input_index\":1"); 39 + T_STR(s, "\"diagnostics\":["); 40 + T_STR(s, "bad method name"); 23 41 T_CHECK(strstr(s, ",}") == NULL); 24 42 T_CHECK(strstr(s, ",]") == NULL); 25 43 free(s);