├── .build.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── assembler ├── assembler.c ├── directives.c └── privatize.c ├── common ├── errors.c ├── expression.c ├── format.c ├── functions.c ├── hashtable.c ├── instructions.c ├── list.c ├── log.c ├── match.c ├── md5.c ├── objects.c ├── operators.c ├── readline.c ├── runtime.c ├── stack.c └── stringop.c ├── doc ├── scas.1.scdoc ├── scdump.1.scdoc ├── scwrap.1.scdoc └── sobj ├── include ├── 8xp.h ├── assembler.h ├── bin.h ├── directives.h ├── errors.h ├── expression.h ├── format.h ├── functions.h ├── hashtable.h ├── instructions.h ├── linker.h ├── list.h ├── log.h ├── match.h ├── md5.h ├── merge.h ├── objects.h ├── operators.h ├── plan9.h ├── privatize.h ├── readline.h ├── runtime.h ├── stack.h └── stringop.h ├── linker ├── 8xp.c ├── bin.c ├── linker.c ├── merge.c └── plan9.c ├── mkfile ├── scas.c ├── scdump.c ├── scwrap.c └── tables ├── amd64.tab ├── arm64.tab ├── generate.c └── z80.tab /.build.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | DEBUGINFOD_URLS: https://debuginfod.archlinux.org 3 | image: archlinux 4 | packages: 5 | - make 6 | - patchrom 7 | - debuginfod 8 | - genkfs 9 | - mktiupgrade 10 | - valgrind 11 | sources: 12 | - https://git.sr.ht/~pixelherodev/scas 13 | - https://github.com/knightos/kernel 14 | - https://github.com/knightos/mkrom 15 | tasks: 16 | - build: | 17 | cd mkrom 18 | make 19 | sudo make install 20 | cd ../scas 21 | make bin/scas 22 | cd ../kernel 23 | # Temporary: use scas PR 24 | git checkout scas 25 | mkdir bin/TI84pSE -p 26 | make AS="valgrind --track-origins=yes --leak-check=full --error-exitcode=1 ../scas/bin/scas" 27 | 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.o 3 | *.6 4 | bin/ 5 | *.rom 6 | test/ 7 | build/ 8 | .lvimrc 9 | .ccls 10 | .ccls-cache 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2020 The KnightOS Group 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-Iinclude/ -O2 -Ibin/ -Wall -Wextra -pedantic -std=c99 -D_XOPEN_SOURCE=700 -D_DEFAULT_SOURCE -g 2 | CC_NATIVE=$(CC) 3 | SO_EXT=so 4 | 5 | ASSEMBLER=assembler/privatize.c assembler/directives.c assembler/assembler.c 6 | COMMON=bin/amd64.c bin/z80.c bin/arm64.c common/functions.c common/hashtable.c common/expression.c common/list.c common/operators.c common/runtime.c common/stringop.c common/errors.c common/stack.c common/format.c common/instructions.c common/log.c common/match.c common/md5.c common/objects.c common/readline.c 7 | LINKER=linker/8xp.c linker/plan9.c linker/bin.c linker/linker.c linker/merge.c 8 | SOURCES=$(ASSEMBLER) $(COMMON) $(LINKER) 9 | 10 | all:bin/scas bin/scdump bin/scwrap 11 | 12 | DESTDIR=/usr/local 13 | SHAREDIR=$(DESTDIR)/share/ 14 | DATADIR=$(SHAREDIR)/knightos/scas/ 15 | MANDIR=$(SHAREDIR)/man/ 16 | BINDIR=$(DESTDIR)/bin/ 17 | LIBDIR=$(DESTDIR)/lib/ 18 | INCDIR=$(DESTDIR)/include/ 19 | 20 | install: all 21 | mkdir -p $(BINDIR) $(INCDIR)/scas/ $(LIBDIR)/ $(DATADIR) 22 | cp bin/scas bin/scwrap bin/scdump $(BINDIR) 23 | cp include/* $(INCDIR)/scas/ 24 | cp tables/amd64.tab tables/z80.tab tables/arm64.tab $(DATADIR) 25 | 26 | install_man: bin/scas.1 bin/scdump.1 bin/scwrap.1 27 | mkdir -p $(MANDIR)/man1/ 28 | cp $^ $(MANDIR)/man1/ 29 | 30 | install_lib_static: bin/scas.a 31 | mkdir -p $(LIBDIR)/ 32 | cp $^ $(LIBDIR)/libscas.a 33 | 34 | install_lib_shared: bin/libscas.$(SO_EXT) 35 | mkdir -p $(LIBDIR)/ 36 | cp $^ $(LIBDIR)/ 37 | 38 | uninstall: 39 | $(RM) $(BINDIR)/scas $(BINDIR)/scdump $(BINDIR)/scwrap -v 40 | $(RM) $(INCDIR)/scas/ -rv 41 | $(RM) $(LIBDIR)/libscas.a 42 | $(RM) $(LIBDIR)/libscas.so 43 | $(RM) $(DATADIR)/amd64.tab 44 | $(RM) $(DATADIR)/arm64.tab 45 | $(RM) $(DATADIR)/z80.tab 46 | $(RM) $(MANDIR)/man1/scas.1 47 | $(RM) $(MANDIR)/man1/scdump.1 48 | $(RM) $(MANDIR)/man1/scwrap.1 49 | 50 | bin/scas.a: $(SOURCES:.c=.o) 51 | $(AR) $(ARFLAGS) $@ $^ 52 | 53 | bin/libscas.$(SO_EXT): bin/scas.a 54 | $(CC) $(LDFLAGS) -shared $^ -o $@ 55 | 56 | bin/scas: scas.o bin/scas.a 57 | $(CC) $(LDFLAGS) $^ -o $@ 58 | 59 | bin/scdump: scdump.o bin/scas.a 60 | $(CC) $(LDFLAGS) $^ -o $@ 61 | 62 | bin/scwrap: scwrap.o bin/scas.a 63 | $(CC) $(LDFLAGS) $^ -o $@ 64 | 65 | bin/z80.c: bin/generate_tables tables/z80.tab 66 | ./bin/generate_tables z80 ./tables/z80.tab ./bin/z80.c ./bin/z80.h 67 | 68 | bin/amd64.c: bin/generate_tables tables/amd64.tab 69 | ./bin/generate_tables amd64 ./tables/amd64.tab ./bin/amd64.c ./bin/amd64.h 70 | 71 | bin/arm64.c: bin/generate_tables tables/arm64.tab 72 | ./bin/generate_tables arm64 ./tables/arm64.tab ./bin/arm64.c ./bin/arm64.h 73 | 74 | bin/arm64.h: bin/arm64.c 75 | bin/amd64.h: bin/amd64.c 76 | bin/z80.h: bin/z80.c 77 | 78 | bin/generate_tables: tables/generate.c 79 | mkdir -p bin/ 80 | $(CC_NATIVE) $(CFLAGS) $< -o $@ 81 | 82 | $(SOURCES:.c=.o) scas.o: bin/z80.h bin/amd64.h bin/arm64.h include/linker.h include/match.h include/format.h include/merge.h include/list.h include/assembler.h include/md5.h include/objects.h include/8xp.h include/plan9.h include/stack.h include/bin.h include/directives.h include/errors.h include/instructions.h include/readline.h include/runtime.h include/stringop.h include/hashtable.h include/functions.h include/log.h include/expression.h include/privatize.h include/operators.h 83 | 84 | .c.o: 85 | $(CC) $(CFLAGS) -c $< -o $@ 86 | 87 | clean: 88 | $(RM) $(SOURCES:.c=.o) scas.o scdump.o scwrap.o bin/ -vr 89 | 90 | bin/scas.1: doc/scas.1.scdoc 91 | mkdir -p bin/ 92 | scdoc < doc/scas.1.scdoc > bin/scas.1 93 | 94 | bin/scdump.1: doc/scdump.1.scdoc 95 | mkdir -p bin/ 96 | scdoc < doc/scdump.1.scdoc > bin/scdump.1 97 | 98 | bin/scwrap.1: doc/scwrap.1.scdoc 99 | mkdir -p bin/ 100 | scdoc < doc/scwrap.1.scdoc > bin/scwrap.1 101 | 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # scas 2 | 3 | [![builds.sr.ht status](https://builds.sr.ht/~pixelherodev/scas/commits.svg)](https://builds.sr.ht/~pixelherodev/scas/commits?) 4 | 5 | Assembler and linker for z80. 6 | 7 | ## Status 8 | 9 | We're finally just about done! All that's needed is a bit more testing before 10 | 1.0 proper can be released. 11 | 12 | ## Compiling from Source 13 | 14 | On a UNIX environment, a standard `make && make install` should suffice to 15 | build the software. If on Windows, we recommend setting up a UNIX environment. 16 | 17 | Now read `man scas` to learn how to use it. 18 | 19 | ## Help, Bugs, Feedback 20 | 21 | If you need help with KnightOS, want to keep up with progress, chat with 22 | developers, or ask any other questions about KnightOS, you can hang out in the 23 | IRC channel: [#knightos on irc.libera.chat](http://web.libera.chat). 24 | 25 | To report bugs, please create [a GitHub issue](https://github.com/KnightOS/KnightOS/issues/new) or contact us on IRC. 26 | 27 | If you'd like to contribute to the project, please see the [contribution guidelines](http://www.knightos.org/contributing). 28 | -------------------------------------------------------------------------------- /assembler/privatize.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "log.h" 7 | #include "list.h" 8 | #include "stack.h" 9 | #include "expression.h" 10 | #include "objects.h" 11 | #include "functions.h" 12 | #include "md5.h" 13 | #include "linker.h" 14 | #include "instructions.h" 15 | #include "runtime.h" 16 | #include "privatize.h" 17 | 18 | void rename_symbol(area_t *a, const char *original, const char *new) { 19 | for (unsigned int i = 0; i < a->late_immediates->length; ++i) { 20 | late_immediate_t *imm = a->late_immediates->items[i]; 21 | for (unsigned int j = 0; j < imm->expression->tokens->length; ++j) { 22 | expression_token_t *tok = imm->expression->tokens->items[j]; 23 | if (tok->type == SYMBOL) { 24 | if (strcasecmp(tok->symbol, original) == 0) { 25 | free(tok->symbol); 26 | tok->symbol = strdup(new); 27 | } 28 | } 29 | } 30 | } 31 | metadata_t *meta = get_area_metadata(a, "scas.functions"); 32 | if (meta != NULL) { 33 | list_t *functions = decode_function_metadata(a, meta->value); 34 | for (unsigned int i = 0; i < functions->length; ++i) { 35 | function_metadata_t *func = functions->items[i]; 36 | if (strcasecmp(func->name, original) == 0) { 37 | free(func->name); 38 | func->name = strdup(new); 39 | } 40 | if (strcasecmp(func->start_symbol, original) == 0) { 41 | free(func->start_symbol); 42 | func->start_symbol = strdup(new); 43 | } 44 | if (strcasecmp(func->end_symbol, original) == 0) { 45 | free(func->end_symbol); 46 | func->end_symbol = strdup(new); 47 | } 48 | } 49 | char *value = encode_function_metadata(functions, &meta->value_length); 50 | list_free(functions); 51 | set_area_metadata(a, "scas.functions", value, meta->value_length); 52 | } 53 | } 54 | 55 | int contains_string(list_t *l, const char *s) { 56 | for (unsigned int i = 0; i < l->length; ++i) { 57 | if (strcasecmp(l->items[i], s) == 0) { 58 | return 1; 59 | } 60 | } 61 | return 0; 62 | } 63 | 64 | void privatize_area(object_t *o, area_t *a, list_t *exports) { 65 | MD5_CTX ctx; 66 | unsigned char raw_checksum[16]; 67 | char checksum[33]; 68 | 69 | MD5_Init(&ctx); 70 | MD5_Update(&ctx, a->data, a->data_length); 71 | MD5_Final(raw_checksum, &ctx); 72 | 73 | unsigned int i; 74 | for (i = 0; i < 16; ++i) { 75 | sprintf(checksum + (i * 2), "%02x", raw_checksum[i]); 76 | } 77 | 78 | scas_log(L_DEBUG, "Privatizing area %s with checksum %s", a->name, checksum); 79 | 80 | for (i = 0; i < a->symbols->length; ++i) { 81 | symbol_t *s = a->symbols->items[i]; 82 | if (contains_string(exports, s->name)) { 83 | scas_log(L_DEBUG, "Found exported symbol '%s'", s->name); 84 | s->exported = 1; 85 | } else { 86 | char *new_name = malloc( 87 | strlen(s->name) + 88 | 1 + 89 | strlen(checksum) + 90 | 1); 91 | char *marker = strcpy(new_name, s->name); 92 | marker = strcat(marker, "@"); 93 | marker = strcat(marker, checksum); 94 | scas_log(L_DEBUG, "Renaming private symbol '%s' to '%s'", s->name, new_name); 95 | for (unsigned int j = 0; j < o->areas->length; ++j) { 96 | area_t *_a = o->areas->items[j]; 97 | rename_symbol(_a, s->name, new_name); 98 | } 99 | free(s->name); 100 | s->name = new_name; 101 | } 102 | } 103 | } 104 | 105 | /* 106 | * Should be done as the last step of assembly. 107 | * Converts unexported symbols into private symbols. 108 | */ 109 | void privatize_symbols(object_t *o) { 110 | if (!scas_runtime.options.explicit_export) { 111 | scas_log(L_DEBUG, "Skipping privitation due to -fno-explicit-export"); 112 | return; 113 | } 114 | 115 | for (unsigned int i = 0; i < o->areas->length; ++i) { 116 | area_t *a = o->areas->items[i]; 117 | privatize_area(o, a, o->exports); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /common/errors.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "list.h" 9 | #include "stack.h" 10 | #include "errors.h" 11 | #include "log.h" 12 | #include "expression.h" 13 | #include "objects.h" 14 | 15 | const char *get_error_string(error_t *error) { 16 | switch (error->code) { 17 | case ERROR_INVALID_INSTRUCTION: 18 | return "Invalid instruction '%s'"; 19 | case ERROR_VALUE_TRUNCATED: 20 | return "Value truncated"; 21 | case ERROR_INVALID_SYNTAX: 22 | return "Invalid syntax"; 23 | case ERROR_INVALID_DIRECTIVE: 24 | return "Invalid directive (%s)"; 25 | case ERROR_UNKNOWN_SYMBOL: 26 | return "Unknown symbol '%s'"; 27 | case ERROR_BAD_FILE: 28 | return "Unable to open '%s' for reading"; 29 | case ERROR_TRAILING_END: 30 | return "No matching if directive"; 31 | case ERROR_DUPLICATE_SYMBOL: 32 | return "Duplicate symbol '%s'"; 33 | default: 34 | return NULL; 35 | } 36 | } 37 | 38 | const char *get_warning_string(warning_t *warning) { 39 | switch (warning->code) { 40 | case WARNING_NO_EFFECT: 41 | return "'%s' has no effect %s"; 42 | default: 43 | return NULL; 44 | } 45 | } 46 | 47 | void add_error(list_t *errors, int code, size_t line_number, const char *line, 48 | int column, const char *file_name, ...) { 49 | error_t *error = malloc(sizeof(error_t)); 50 | error->code = code; 51 | error->line_number = line_number; 52 | error->file_name = strdup(file_name); 53 | error->line = strdup(line); 54 | error->column = column; 55 | 56 | const char *fmt = get_error_string(error); 57 | 58 | va_list args; 59 | va_start(args, file_name); 60 | int len = vsnprintf(NULL, 0, fmt, args); 61 | va_end(args); 62 | 63 | char *buf = malloc(len + 1); 64 | va_start(args, file_name); 65 | vsnprintf(buf, len + 1, fmt, args); 66 | va_end(args); 67 | 68 | error->message = buf; 69 | 70 | list_add(errors, error); 71 | scas_log(L_DEBUG, "Added error '%s' at %s:%d:%d", buf, file_name, 72 | line_number, column); 73 | } 74 | 75 | void add_warning(list_t *warnings, int code, size_t line_number, 76 | const char *line, int column, const char *file_name, ...) { 77 | warning_t *warning = malloc(sizeof(warning_t)); 78 | warning->code = code; 79 | warning->line_number = line_number; 80 | warning->file_name = strdup(file_name); 81 | warning->line = strdup(line); 82 | warning->column = column; 83 | 84 | const char *fmt = get_warning_string(warning); 85 | 86 | va_list args; 87 | va_start(args, file_name); 88 | int len = vsnprintf(NULL, 0, fmt, args); 89 | va_end(args); 90 | 91 | char *buf = malloc(len + 1); 92 | va_start(args, file_name); 93 | vsnprintf(buf, len + 1, fmt, args); 94 | va_end(args); 95 | 96 | warning->message = buf; 97 | 98 | list_add(warnings, warning); 99 | scas_log(L_DEBUG, "Added warning '%s' at %s:%d:%d", buf, file_name, 100 | line_number, column); 101 | } 102 | 103 | /* Locates the address in the source maps provided to get the file name and line number */ 104 | void add_error_from_map(list_t *errors, int code, list_t *maps, uint64_t address, ...) { 105 | source_map_t *map; 106 | source_map_entry_t *entry; 107 | bool found = false; 108 | for (unsigned int i = 0; i < maps->length; ++i) { 109 | map = maps->items[i]; 110 | for (unsigned int j = 0; j < map->entries->length; ++j) { 111 | entry = map->entries->items[j]; 112 | if (address >= entry->address && address < entry->address + entry->length) { 113 | found = true; 114 | break; 115 | } 116 | } 117 | if (found) { 118 | break; 119 | } 120 | } 121 | 122 | error_t *error = malloc(sizeof(error_t)); 123 | error->code = code; 124 | error->column = 0; 125 | 126 | const char *fmt = get_error_string(error); 127 | 128 | va_list args; 129 | va_start(args, address); 130 | int len = vsnprintf(NULL, 0, fmt, args); 131 | va_end(args); 132 | 133 | char *buf = malloc(len + 1); 134 | va_start(args, address); 135 | vsnprintf(buf, len + 1, fmt, args); 136 | va_end(args); 137 | 138 | error->message = buf; 139 | scas_log(L_DEBUG, "Added error '%s' at:", buf); 140 | if (found) { 141 | error->line_number = entry->line_number; 142 | error->file_name = strdup(map->file_name); 143 | error->line = strdup(entry->source_code); 144 | for (unsigned int i = 0; i < maps->length; ++i) { 145 | map = maps->items[i]; 146 | for (unsigned int j = 0; j < map->entries->length; ++j) { 147 | entry = map->entries->items[j]; 148 | if (address >= entry->address && address < entry->address + entry->length) { 149 | scas_log(L_ERROR, "\t%s:%d:%d", map->file_name, 150 | entry->line_number, error->column); 151 | } 152 | } 153 | } 154 | } else { 155 | error->line_number = 0; 156 | error->file_name = NULL; 157 | error->line = NULL; 158 | } 159 | list_add(errors, error); 160 | } 161 | -------------------------------------------------------------------------------- /common/expression.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "list.h" 9 | #include "stack.h" 10 | #include "expression.h" 11 | #include "log.h" 12 | #include "readline.h" 13 | #include "stringop.h" 14 | #include "operators.h" 15 | #include "objects.h" 16 | 17 | static operator_t operators[] = { 18 | { "+", OP_UNARY_PLUS, 1, 3, 1, operator_unary_plus }, 19 | { "-", OP_UNARY_MINUS, 1, 3, 1, operator_unary_minus }, 20 | { "~", OP_NEGATE, 1, 3, 1, operator_negate }, 21 | { "!", OP_LOGICAL_NOT, 1, 3, 1, operator_logical_not }, 22 | { "*", OP_MULTIPLY, 0, 5, 0, operator_multiply }, 23 | { "/", OP_DIVIDE, 0, 5, 0, operator_divide }, 24 | { "%", OP_MODULO, 0, 5, 0, operator_modulo }, 25 | { "+", OP_PLUS, 0, 6, 0, operator_add }, 26 | { "-", OP_MINUS, 0, 6, 0, operator_subtract }, 27 | { "<<", OP_LEFT_SHIFT, 0, 7, 0, operator_left_shift }, 28 | { ">>", OP_RIGHT_SHIFT, 0, 7, 0, operator_right_shift }, 29 | { "<=", OP_LESS_THAN_OR_EQUAL_TO, 0, 8, 0, operator_less_than_or_equal_to }, 30 | { ">=", OP_GREATER_THAN_OR_EQUAL_TO, 0, 8, 0, operator_greater_than_or_equal_to }, 31 | { "<", OP_LESS_THAN, 0, 8, 0, operator_less_than }, 32 | { ">", OP_GREATER_THAN, 0, 8, 0, operator_greater_than }, 33 | { "==", OP_EQUAL_TO, 0, 9, 0, operator_equal_to }, 34 | { "!=", OP_NOT_EQUAL_TO, 0, 9, 0, operator_not_equal_to }, 35 | { "&", OP_BITWISE_AND, 0, 10, 0, operator_bitwise_and }, 36 | { "^", OP_BITWISE_XOR, 0, 11, 0, operator_bitwise_xor }, 37 | { "|", OP_BITWISE_OR, 0, 12, 0, operator_bitwise_or }, 38 | { "&&", OP_LOGICAL_AND, 0, 13, 0, operator_logical_and }, 39 | { "||", OP_LOGICAL_OR, 0, 14, 0, operator_logical_or }, 40 | }; 41 | 42 | void print_tokenized_expression(FILE *f, tokenized_expression_t *expression) { 43 | for (unsigned int i = 0; i < expression->tokens->length; ++i) { 44 | expression_token_t *token = expression->tokens->items[i]; 45 | switch (token->type) { 46 | case SYMBOL: 47 | fprintf(f, "'%s'", token->symbol); 48 | break; 49 | case NUMBER: 50 | fprintf(f, "0x%04X", (unsigned int)token->number); 51 | break; 52 | case OPERATOR: 53 | fprintf(f, "[(%snary) %s]", operators[token->operator].is_unary ? "u" : 54 | "bi", operators[token->operator].operator); 55 | break; 56 | } 57 | if (i != expression->tokens->length - 1) { 58 | fputc(' ', f); 59 | } 60 | } 61 | } 62 | 63 | void fwrite_tokens(FILE *f, tokenized_expression_t *expression) { 64 | uint32_t len = expression->tokens->length; 65 | fwrite(&len, sizeof(uint32_t), 1, f); 66 | for (unsigned int i = 0; i < expression->tokens->length; ++i) { 67 | expression_token_t *token = expression->tokens->items[i]; 68 | fputc(token->type, f); 69 | switch (token->type) { 70 | case SYMBOL: 71 | fprintf(f, "%s", token->symbol); 72 | fputc(0, f); 73 | break; 74 | case NUMBER: 75 | fwrite(&token->number, sizeof(uint64_t), 1, f); 76 | break; 77 | case OPERATOR: 78 | fputc(token->operator, f); 79 | break; 80 | } 81 | } 82 | } 83 | 84 | tokenized_expression_t *fread_tokenized_expression(FILE *f) { 85 | uint32_t len; 86 | if (fread (&len, 1, sizeof(uint32_t), f) != sizeof(uint32_t)) { 87 | scas_log(L_ERROR, "Failed to read expression length from file"); 88 | return NULL; 89 | } 90 | tokenized_expression_t *expression = malloc(sizeof(tokenized_expression_t)); 91 | expression->tokens = create_list(); 92 | expression->symbols = NULL; 93 | for (uint32_t i = 0; i < len; ++i) { 94 | expression_token_t *token = malloc(sizeof(expression_token_t)); 95 | token->type = fgetc(f); 96 | switch (token->type) { 97 | case SYMBOL: 98 | token->symbol = read_line(f); 99 | break; 100 | case NUMBER: 101 | if (fread(&token->number, 1, sizeof(uint64_t), f) != sizeof(uint64_t)) { 102 | scas_log(L_ERROR, "Failed to read token number from file"); 103 | free(token); 104 | free_expression(expression); 105 | return NULL; 106 | } 107 | break; 108 | case OPERATOR: 109 | token->operator = fgetc(f); 110 | break; 111 | } 112 | list_add(expression->tokens, token); 113 | } 114 | return expression; 115 | } 116 | 117 | uint64_t evaluate_expression(tokenized_expression_t *expression, list_t 118 | *symbols, int *error, char **symbol) { 119 | stack_type *stack = create_stack(); 120 | list_t *to_free = create_list(); 121 | expression_token_t *resolved; 122 | operator_t op; 123 | uint64_t res = 0; 124 | *error = 0; 125 | 126 | for (unsigned int i = 0; i < expression->tokens->length; ++i) { 127 | expression_token_t *token = expression->tokens->items[i]; 128 | switch (token->type) { 129 | case SYMBOL: 130 | resolved = malloc(sizeof(expression_token_t)); 131 | resolved->type = NUMBER; 132 | resolved->number = 0; 133 | 134 | bool found = false; 135 | for (unsigned int j = 0; j < symbols->length; ++j) { 136 | symbol_t *sym = symbols->items[j]; 137 | if (strcasecmp(sym->name, token->symbol) == 0) { 138 | resolved->number = sym->value; 139 | found = true; 140 | break; 141 | } 142 | } 143 | if (!found) { 144 | *symbol = token->symbol; 145 | *error = EXPRESSION_BAD_SYMBOL; 146 | } 147 | 148 | list_add(to_free, resolved); 149 | stack_push(stack, resolved); 150 | break; 151 | case NUMBER: 152 | stack_push(stack, token); 153 | break; 154 | case OPERATOR: 155 | op = operators[token->operator]; 156 | resolved = malloc(sizeof(expression_token_t)); 157 | resolved->type = NUMBER; 158 | resolved->number = op.function(stack, error); 159 | list_add(to_free, resolved); 160 | stack_push(stack, resolved); 161 | break; 162 | } 163 | } 164 | 165 | if (stack->length == 0) { 166 | *error = EXPRESSION_BAD_SYMBOL; 167 | } else { 168 | expression_token_t *token = stack_pop(stack); 169 | if (token->type != NUMBER) { 170 | *error = EXPRESSION_BAD_SYMBOL; 171 | } else { 172 | res = token->number; 173 | } 174 | } 175 | stack_free(stack); 176 | free_flat_list(to_free); 177 | return res; 178 | } 179 | 180 | expression_token_t *parse_digit(const char **string) { 181 | int base = 10; 182 | if ((*string)[0] != 0 && (*string)[0] == '0') { 183 | switch ((*string)[1]) { 184 | case 'b': 185 | base = 2; 186 | break; 187 | case 'x': 188 | base = 16; 189 | break; 190 | case 'o': 191 | base = 8; 192 | break; 193 | case 0: 194 | // It's probably a single digit number 195 | --*string; 196 | break; 197 | default: 198 | return 0; 199 | break; 200 | } 201 | *string += 2; 202 | 203 | expression_token_t *expr = malloc(sizeof(expression_token_t)); 204 | expr->type = NUMBER; 205 | char *end; 206 | expr->number = strtol(*string, &end, base); 207 | *string = end; 208 | return expr; 209 | } 210 | 211 | /* Character literals */ 212 | if ((*string)[0] == '\'') { 213 | if (strlen(*string) >= 3) { 214 | char *end = strrchr(*string, '\''); 215 | if (end) { 216 | char *temp = malloc(end - *string + 1); 217 | strncpy(temp, *string, end - *string); 218 | temp[end - *string] = '\0'; 219 | unescape_string(temp); 220 | expression_token_t *expr = malloc(sizeof(expression_token_t)); 221 | expr->type = NUMBER; 222 | expr->number = (uint64_t)temp[1]; 223 | free(temp); 224 | *string = end + 1; 225 | return expr; 226 | } 227 | } 228 | } 229 | 230 | /* ASxxxx-style local labels */ 231 | const char *a = *string; 232 | while (*a) { 233 | if (*a == '$') { 234 | return NULL; 235 | } 236 | ++a; 237 | } 238 | 239 | /* Plain decimal number */ 240 | expression_token_t *expr = malloc(sizeof(expression_token_t)); 241 | expr->type = NUMBER; 242 | char *end; 243 | expr->number = strtol(*string, &end, 10); 244 | *string = end; 245 | return expr; 246 | } 247 | 248 | expression_token_t *parse_operator(const char **string, int is_unary) { 249 | for (size_t i = 0; i < sizeof(operators) / sizeof(operator_t); i++) { 250 | operator_t op = operators[i]; 251 | if (op.is_unary == is_unary && strncmp(op.operator, *string, strlen(op.operator)) == 0) { 252 | expression_token_t *exp = malloc(sizeof(expression_token_t)); 253 | exp->type = OPERATOR; 254 | exp->operator = i; 255 | *string += strlen(op.operator); 256 | return exp; 257 | } 258 | } 259 | 260 | return 0; 261 | } 262 | 263 | expression_token_t *parse_symbol(const char **string) { 264 | const char *end = *string; 265 | while (*end && (isalnum(*end) || *end == '_' || *end == '.' || *end == '@' || *end == '$')) { 266 | end++; 267 | } 268 | char *symbol = malloc(end - *string + 1); 269 | strncpy(symbol, *string, end - *string); 270 | symbol[end - *string] = '\0'; 271 | *string = end; 272 | 273 | expression_token_t *expr = malloc(sizeof(expression_token_t)); 274 | expr->type = SYMBOL; 275 | expr->symbol = symbol; 276 | return expr; 277 | } 278 | 279 | enum { 280 | STATE_VALUE, 281 | STATE_OPERATOR, 282 | }; 283 | 284 | void free_expression(tokenized_expression_t *expression) { 285 | for (unsigned int i = 0; i < expression->tokens->length; i += 1) { 286 | free_expression_token((expression_token_t*)expression->tokens->items[i]); 287 | } 288 | list_free(expression->tokens); 289 | if (expression->symbols) { 290 | free_flat_list(expression->symbols); 291 | } 292 | free(expression); 293 | } 294 | 295 | // Based on shunting-yard 296 | tokenized_expression_t *parse_expression(const char *str) { 297 | const size_t op_count = sizeof(operators) / sizeof(operator_t); 298 | char *operator_cache = malloc(op_count + 1); 299 | for (size_t i = 0; i < op_count; i++) { 300 | operator_t op = operators[i]; 301 | operator_cache[i] = op.operator[0]; 302 | } 303 | operator_cache[op_count] = 0; 304 | 305 | int tokenizer_state = STATE_OPERATOR; 306 | tokenized_expression_t *list = malloc(sizeof(tokenized_expression_t)); 307 | list->tokens = create_list(); 308 | list->symbols = create_list(); 309 | stack_type *stack = create_stack(); 310 | 311 | const char *current = str; 312 | while (1) { 313 | while (*current == '#' || isspace(*(current))) { 314 | /* Note: # is used for immediate data in ASxxxx, it's somewhat superfluous */ 315 | current++; 316 | } 317 | expression_token_t *expr; 318 | if (*current == 0) { 319 | break; 320 | } else if (isdigit(*current) || *current == '\'') { 321 | if (tokenizer_state == STATE_VALUE) { 322 | free_expression(list); 323 | list = NULL; 324 | goto exit; 325 | } 326 | 327 | expr = parse_digit(¤t); 328 | if (expr == NULL) { 329 | expr = parse_symbol(¤t); 330 | list_add(list->symbols, strdup(expr->symbol)); 331 | } 332 | tokenizer_state = STATE_VALUE; 333 | } else if (*current == '(') { 334 | if (tokenizer_state == STATE_VALUE) { 335 | free_expression(list); 336 | list = NULL; 337 | goto exit; 338 | } 339 | expr = malloc(sizeof(expression_token_t)); 340 | expr->type = OPEN_PARENTHESIS; 341 | stack_push(stack, expr); 342 | current++; 343 | tokenizer_state = STATE_OPERATOR; 344 | continue; 345 | } else if (*current == ')') { 346 | if (stack->length == 0 || tokenizer_state == STATE_OPERATOR) { 347 | free_expression(list); 348 | list = NULL; 349 | goto exit; 350 | } 351 | expr = stack->items[stack->length - 1]; 352 | while (expr && expr->type != OPEN_PARENTHESIS) { 353 | stack_pop(stack); 354 | list_add(list->tokens, expr); 355 | 356 | if (stack->length <= 0) { 357 | expr = 0; 358 | free_expression(list); 359 | list = NULL; 360 | goto exit; 361 | } 362 | 363 | expr = stack_peek(stack); 364 | } 365 | 366 | stack_pop(stack); 367 | free(expr); 368 | current++; 369 | tokenizer_state = STATE_VALUE; 370 | continue; 371 | } else if(strchr(operator_cache, *current)) { 372 | expr = parse_operator(¤t, tokenizer_state == STATE_OPERATOR); 373 | if (expr == 0) { 374 | free_expression(list); 375 | list = NULL; 376 | goto exit; 377 | } 378 | tokenizer_state = STATE_OPERATOR; 379 | } else { 380 | if (tokenizer_state == STATE_VALUE) { 381 | free_expression(list); 382 | list = NULL; 383 | goto exit; 384 | } 385 | expr = parse_symbol(¤t); 386 | list_add(list->symbols, strdup(expr->symbol)); 387 | tokenizer_state = STATE_VALUE; 388 | } 389 | 390 | if (!expr) { 391 | free_expression(list); 392 | list = NULL; 393 | goto exit; 394 | } 395 | 396 | if (expr->type == OPERATOR) { 397 | operator_t *operator = &operators[expr->operator]; 398 | expression_token_t *expr2 = stack->length ? stack_peek(stack) : 0; 399 | operator_t *operator2 = expr2 ? &operators[expr2->operator] : 0; 400 | while (expr2 && expr2->type == OPERATOR && ((!operator->right_assocative && operator2->precedence == operator->precedence) || operator->precedence > operator2->precedence)) { 401 | stack_pop(stack); 402 | list_add(list->tokens, expr2); 403 | if (!stack->length) { 404 | break; 405 | } 406 | expr2 = stack_peek(stack); 407 | operator2 = &operators[expr2->operator]; 408 | } 409 | stack_push(stack, expr); 410 | } else { 411 | list_add(list->tokens, expr); 412 | } 413 | } 414 | 415 | while (stack->length > 0) { 416 | expression_token_t *item = stack_pop(stack); 417 | list_add(list->tokens, item); 418 | } 419 | 420 | exit: 421 | stack_free(stack); 422 | free(operator_cache); 423 | return list; 424 | 425 | } 426 | 427 | void free_expression_token(expression_token_t *token) { 428 | if (token->type == SYMBOL) { 429 | free(token->symbol); 430 | } 431 | free(token); 432 | } 433 | 434 | /* add a sequence of +s and -s to get an offset for a relative label. */ 435 | int get_relative_label_offset(tokenized_expression_t *expression, unsigned int *start) { 436 | int offset = 0; 437 | 438 | for(++*start; *start < expression->tokens->length; ++*start) { 439 | expression_token_t *token = expression->tokens->items[*start]; 440 | 441 | if (token->type != OPERATOR) { 442 | --*start; /* roll back */ 443 | break; 444 | } 445 | operator_t op = operators[token->operator]; 446 | if (op.expression_type == OP_UNARY_PLUS) { 447 | offset++; 448 | } else if(op.expression_type == OP_UNARY_MINUS) { 449 | offset--; 450 | } else { 451 | --*start; /* roll back */ 452 | break; 453 | } 454 | } 455 | 456 | if (offset > 0) { 457 | offset--; 458 | } 459 | 460 | return offset; 461 | } 462 | -------------------------------------------------------------------------------- /common/format.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "format.h" 7 | 8 | typedef enum { 9 | S_default = 0, 10 | S_unsigned = 1, 11 | S_lower = 2, 12 | S_prefix = 4 13 | } spec_flags; 14 | 15 | typedef enum { 16 | T_char, 17 | T_short, 18 | T_int, 19 | T_long, 20 | T_llong, 21 | T_size, 22 | T_intptr, 23 | T_ptrdiff, 24 | T_intmax 25 | } spec_type; 26 | 27 | struct format_spec { 28 | int base, width; 29 | spec_flags flags; 30 | spec_type type; 31 | }; 32 | 33 | static uintmax_t get_value(struct format_spec spec, uintmax_t (*getarg)(size_t)) { 34 | if (spec.flags & S_unsigned) { 35 | switch (spec.type) { 36 | case T_char: 37 | return (uintmax_t)(char)getarg(sizeof(int)); 38 | case T_short: 39 | return (uintmax_t)(short)getarg(sizeof(int)); 40 | case T_int: 41 | return (uintmax_t)getarg(sizeof(unsigned int)); 42 | case T_long: 43 | return (uintmax_t)getarg(sizeof(unsigned long)); 44 | case T_llong: 45 | return (uintmax_t)getarg(sizeof(unsigned long long)); 46 | case T_size: 47 | return (uintmax_t)getarg(sizeof(size_t)); 48 | case T_intptr: 49 | return (uintmax_t)getarg(sizeof(uintptr_t)); 50 | case T_ptrdiff: 51 | return (uintmax_t)getarg(sizeof(ptrdiff_t)); 52 | case T_intmax: 53 | return getarg(sizeof(uintmax_t)); 54 | } 55 | } else { 56 | switch (spec.type) { 57 | case T_char: 58 | return (uintmax_t)(unsigned char)getarg(sizeof(int)); 59 | case T_short: 60 | return (uintmax_t)(unsigned short)getarg(sizeof(int)); 61 | case T_int: 62 | return (uintmax_t)getarg(sizeof(int)); 63 | case T_long: 64 | return (uintmax_t)getarg(sizeof(long)); 65 | case T_llong: 66 | return (uintmax_t)getarg(sizeof(long long)); 67 | case T_size: 68 | return (uintmax_t)getarg(sizeof(size_t)); 69 | case T_intptr: 70 | return (uintmax_t)getarg(sizeof(intptr_t)); 71 | case T_ptrdiff: 72 | return (uintmax_t)getarg(sizeof(ptrdiff_t)); 73 | case T_intmax: 74 | return getarg(sizeof(uintmax_t)); 75 | } 76 | } 77 | return 0; 78 | } 79 | 80 | static void putstr(void (*putchar)(char), char *str) { 81 | while (*str) { 82 | putchar(*str); 83 | ++str; 84 | } 85 | } 86 | 87 | static void handle_spec(void (*putchar)(char), uintmax_t (*getarg)(size_t), const char **f) { 88 | struct format_spec spec = { 89 | .base = 10, 90 | .width = 0, 91 | .flags = S_default, 92 | .type = T_int 93 | }; 94 | 95 | ++*f; 96 | 97 | bool repeat; 98 | do { 99 | repeat = false; 100 | 101 | switch (**f) { 102 | case 'd': /* fallthrough */ 103 | case 'i': 104 | spec.base = 10; 105 | break; 106 | case 'o': 107 | spec.base = 8; 108 | spec.flags |= S_unsigned; 109 | break; 110 | case 'x': 111 | spec.base = 16; 112 | spec.flags |= (S_lower | S_unsigned); 113 | break; 114 | case 'X': 115 | spec.base = 16; 116 | spec.flags |= S_unsigned; 117 | break; 118 | case 'p': 119 | spec.base = 16; 120 | spec.flags |= (S_prefix | S_unsigned | S_lower); 121 | spec.type = T_intptr; 122 | break; 123 | case 'c': 124 | { 125 | char c = getarg(sizeof(int)); 126 | putchar(c); 127 | return; 128 | } 129 | case 's': 130 | { 131 | char *s = (char *)getarg(sizeof(char *)); 132 | if (!s) { 133 | putstr(putchar, "(nil)"); 134 | } else { 135 | putstr(putchar, s); 136 | } 137 | return; 138 | } 139 | case 'z': 140 | spec.type = T_size; 141 | repeat = true; 142 | ++*f; 143 | break; 144 | case '%': 145 | putchar('%'); 146 | return; 147 | default: 148 | if (isdigit(**f)) { 149 | char *endptr; 150 | spec.width = strtol(*f, &endptr, 10); 151 | *f += endptr - *f; 152 | repeat = true; 153 | } 154 | break; 155 | } 156 | } while (repeat); 157 | 158 | char buffer[32]; 159 | // TODO: Deal with things that are not integers 160 | 161 | uintmax_t val = get_value(spec, getarg); 162 | 163 | const char *digits = "0123456789ABCDEF"; 164 | 165 | if (spec.base != 0) { 166 | int i = 0; 167 | uintmax_t value = val; 168 | if (!(spec.flags & S_unsigned)) { 169 | if ((intmax_t)value < 0) { 170 | value = (uintmax_t)-(intmax_t)value; 171 | } 172 | } 173 | if (spec.type == T_intptr && value == 0) { 174 | putstr(putchar, "(nil)"); 175 | return; 176 | } else { 177 | do { 178 | char d = digits[value % spec.base]; 179 | if (spec.flags & S_lower) { 180 | d = tolower(d); 181 | } 182 | buffer[i++] = d; 183 | value /= spec.base; 184 | } while (value); 185 | } 186 | if (!(spec.flags & S_unsigned) && (intmax_t)val < 0) { 187 | putchar('-'); 188 | } 189 | if (spec.flags & S_prefix) { 190 | if (spec.base == 16 && val != 0) { 191 | buffer[i++] = 'x'; 192 | buffer[i++] = '0'; 193 | } 194 | if (spec.base == 8) { 195 | buffer[i++] = '0'; 196 | } 197 | } 198 | if (i < spec.width) { 199 | for (int j = 0; j < spec.width - i; ++j) { 200 | putchar('0'); 201 | } 202 | } 203 | for (--i; i >= 0; --i) { 204 | putchar(buffer[i]); 205 | } 206 | } 207 | } 208 | 209 | void format(void (*putchar)(char), uintmax_t (*getarg)(size_t size), const char *fmt) { 210 | while (*fmt) { 211 | if (*fmt == '%') { 212 | handle_spec(putchar, getarg, &fmt); 213 | ++fmt; 214 | } else { 215 | putchar(*fmt); 216 | ++fmt; 217 | } 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /common/functions.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "list.h" 8 | #include "stack.h" 9 | #include "expression.h" 10 | #include "objects.h" 11 | #include "functions.h" 12 | #include "hashtable.h" 13 | #include "log.h" 14 | 15 | unsigned int hash(void *data) { 16 | unsigned int hash = 5381; 17 | char *str = data; 18 | int c; 19 | while ((c = *str++)) { 20 | hash = ((hash << 5) + hash) + c; 21 | } 22 | return hash; 23 | } 24 | 25 | symbol_t *get_symbol_by_name(area_t *area, const char *name) { 26 | unsigned int i; 27 | for (i = 0; i < area->symbols->length; ++i) { 28 | symbol_t *sym = area->symbols->items[i]; 29 | if (strcasecmp(sym->name, name) == 0) { 30 | return sym; 31 | } 32 | } 33 | return NULL; 34 | } 35 | 36 | function_metadata_t *get_function_for_address(area_t *area, list_t *functions, uint32_t address) { 37 | unsigned int i; 38 | for (i = 0; i < functions->length; ++i) { 39 | function_metadata_t *func = functions->items[i]; 40 | if (func->area == area && func->start_address <= address && func->end_address >= address) { 41 | return func; 42 | } 43 | } 44 | return NULL; 45 | } 46 | 47 | function_metadata_t *get_function_by_name(list_t *functions, const char *name) { 48 | unsigned int i; 49 | for (i = 0; i < functions->length; ++i) { 50 | function_metadata_t *func = functions->items[i]; 51 | if (strcasecmp(func->name, name) == 0) { 52 | return func; 53 | } 54 | } 55 | return NULL; 56 | } 57 | 58 | void mark_dependencies_precious(function_metadata_t *parent, list_t *functions, object_t *object); 59 | 60 | void mark_precious(list_t *functions, late_immediate_t *imm, object_t *object, int recurse) { 61 | for (unsigned int j = 0; j < imm->expression->tokens->length; ++j) { 62 | expression_token_t *tok = imm->expression->tokens->items[j]; 63 | if (tok->type == SYMBOL) { 64 | function_metadata_t *func = get_function_by_name(functions, tok->symbol); 65 | if (func) { 66 | if (!func->precious) { 67 | scas_log(L_DEBUG, "Marked %s as precious", func->name); 68 | func->precious = 1; 69 | if (recurse) { 70 | mark_dependencies_precious(func, functions, object); 71 | } 72 | } 73 | } 74 | } 75 | } 76 | } 77 | 78 | void mark_dependencies_precious(function_metadata_t *parent, list_t *functions, object_t *object) { 79 | scas_log(L_DEBUG, "Marking dependencies of %s as precious", parent->name); 80 | for (unsigned int i = 0; i < object->areas->length; ++i) { 81 | area_t *area = object->areas->items[i]; 82 | for (unsigned int j = 0; j < area->late_immediates->length; ++j) { 83 | late_immediate_t *imm = area->late_immediates->items[j]; 84 | if (imm->base_address >= parent->start_address && imm->base_address <= parent->end_address) { 85 | mark_precious(functions, imm, object, 1); 86 | } 87 | } 88 | } 89 | } 90 | 91 | int compare_functions(const void *a, const void *b) { 92 | const function_metadata_t *func_a = a; 93 | const function_metadata_t *func_b = b; 94 | return func_a->start_address - func_b->start_address; 95 | } 96 | 97 | void remove_unused_functions(object_t *object) { 98 | scas_log(L_DEBUG, "Optimizing out unused functions for object"); 99 | list_t *functions = create_list(); 100 | unsigned int i; 101 | for (i = 0; i < object->areas->length; ++i) { 102 | area_t *area = object->areas->items[i]; 103 | metadata_t *meta = get_area_metadata(area, "scas.functions"); 104 | if (meta) { 105 | list_t *decoded = decode_function_metadata(area, meta->value); 106 | list_cat(functions, decoded); 107 | list_free(decoded); 108 | } 109 | } 110 | for (i = 0; i < functions->length; ++i) { 111 | function_metadata_t *meta = functions->items[i]; 112 | meta->precious = 0; 113 | symbol_t *start = get_symbol_by_name(meta->area, meta->start_symbol); 114 | symbol_t *end = get_symbol_by_name(meta->area, meta->end_symbol); 115 | if (!start || !end) { 116 | scas_log(L_ERROR, "Warning: function %s has unknown start and end symbols", meta->name); 117 | function_metadata_t *func = functions->items[i]; 118 | free(func->name); 119 | free(func->start_symbol); 120 | free(func->end_symbol); 121 | free(func); 122 | list_del(functions, i); 123 | --i; 124 | } else { 125 | scas_log(L_DEBUG, "Function %s is located at %08X-%08X", meta->name, start->value, end->value); 126 | meta->start_address = start->value; 127 | meta->end_address = end->value; 128 | } 129 | } 130 | scas_log(L_DEBUG, "Found %d functions, building dependency map", functions->length); 131 | for (i = 0; i < object->areas->length; ++i) { 132 | area_t *a = object->areas->items[i]; 133 | for (unsigned int j = 0; j < a->late_immediates->length; ++j) { 134 | late_immediate_t *imm = a->late_immediates->items[j]; 135 | function_metadata_t *meta = get_function_for_address(a, functions, imm->address); 136 | if (meta == NULL) { // Precious code 137 | mark_precious(functions, imm, object, 0); 138 | } 139 | } 140 | } 141 | for (i = 0; i < functions->length; ++i) { 142 | function_metadata_t *func = functions->items[i]; 143 | if (func->precious) { 144 | mark_dependencies_precious(func, functions, object); 145 | } 146 | } 147 | for (i = 0; i < functions->length; ++i) { 148 | function_metadata_t *func = functions->items[i]; 149 | if (!func->precious) { 150 | scas_log(L_DEBUG, "Removing unused function %s (at %08X - %08X)", func->name, func->start_address, func->end_address); 151 | int length = func->end_address - func->start_address; 152 | delete_from_area(func->area, func->start_address, length); 153 | for (unsigned int k = 0; k < func->area->symbols->length; ++k) { 154 | symbol_t *sym = func->area->symbols->items[k]; 155 | if (sym->type == SYMBOL_LABEL) { 156 | if (sym->value >= func->start_address && sym->value < func->end_address) { 157 | list_del(func->area->symbols, k); 158 | k -= 1; 159 | } else if (sym->value >= func->end_address) { 160 | sym->value -= length; 161 | } 162 | } 163 | } 164 | unsigned int j; 165 | for (j = 0; j < func->area->late_immediates->length; ++j) { 166 | late_immediate_t *_imm = func->area->late_immediates->items[j]; 167 | if (_imm->address >= func->start_address && _imm->address < func->end_address) { 168 | list_del(func->area->late_immediates, j--); 169 | } else if (_imm->address >= func->end_address) { 170 | _imm->base_address -= length; 171 | _imm->instruction_address -= length; 172 | _imm->address -= length; 173 | } 174 | } 175 | for (j = 0; j < func->area->source_map->length; ++j) { 176 | source_map_t *map = func->area->source_map->items[j]; 177 | for (unsigned int k = 0; k < map->entries->length; ++k) { 178 | source_map_entry_t *entry = map->entries->items[k]; 179 | if (entry->address >= func->start_address && entry->address < func->end_address) { 180 | free(entry->source_code); 181 | free(entry); 182 | list_del(map->entries, k--); 183 | } else if (entry->address >= func->end_address) { 184 | entry->address -= length; 185 | } 186 | } 187 | } 188 | for (unsigned int k = 0; k < functions->length; ++k) { 189 | function_metadata_t *_func = functions->items[k]; 190 | if (_func->start_address >= func->end_address) { 191 | _func->start_address -= length; 192 | _func->end_address -= length; 193 | } 194 | } 195 | } 196 | } 197 | for (unsigned int i = 0; i < functions->length; i++) { 198 | function_metadata_t *func = functions->items[i]; 199 | free(func->name); 200 | free(func->start_symbol); 201 | free(func->end_symbol); 202 | free(func); 203 | } 204 | list_free(functions); 205 | } 206 | 207 | list_t *decode_function_metadata(area_t *area, char *value) { 208 | uint32_t total; 209 | list_t *result = create_list(); 210 | total = *(uint32_t *)value; 211 | value += sizeof(uint32_t); 212 | 213 | if (total > 10000) { 214 | scas_log(L_ERROR, "More than 10,000 functions detected. This is probably an internal error."); 215 | list_free(result); 216 | return NULL; 217 | } 218 | scas_log(L_DEBUG, "Decoding metadata for %d functions", (int)total); 219 | for (uint32_t i = 0; i < total; ++i) { 220 | uint32_t len; 221 | function_metadata_t *meta = calloc(1, sizeof(function_metadata_t)); 222 | meta->area = area; 223 | len = *(uint32_t *)value; 224 | value += sizeof(uint32_t); 225 | meta->name = malloc(len + 1); 226 | strcpy(meta->name, value); 227 | value += len + 1; 228 | 229 | len = *(uint32_t *)value; 230 | value += sizeof(uint32_t); 231 | meta->start_symbol = malloc(len + 1); 232 | strcpy(meta->start_symbol, value); 233 | value += len + 1; 234 | 235 | len = *(uint32_t *)value; 236 | value += sizeof(uint32_t); 237 | meta->end_symbol = malloc(len + 1); 238 | strcpy(meta->end_symbol, value); 239 | value += len + 1; 240 | 241 | list_add(result, meta); 242 | } 243 | return result; 244 | } 245 | 246 | char *encode_function_metadata(list_t *metadata, uint64_t *value_length) { 247 | uint32_t len = sizeof(uint32_t); 248 | unsigned int i; 249 | for (i = 0; i < metadata->length; ++i) { 250 | function_metadata_t *meta = metadata->items[i]; 251 | len += strlen(meta->name) + 1 + sizeof(uint32_t); 252 | len += strlen(meta->start_symbol) + 1 + sizeof(uint32_t); 253 | len += strlen(meta->end_symbol) + 1 + sizeof(uint32_t); 254 | } 255 | char *result = malloc(len); 256 | scas_log(L_DEBUG, "Allocated %d bytes for function metadata", len); 257 | *value_length = (uint32_t)len; 258 | *(uint32_t *)result = (uint32_t)metadata->length; 259 | int ptr = sizeof(uint32_t); 260 | for (i = 0; i < metadata->length; ++i) { 261 | function_metadata_t *meta = metadata->items[i]; 262 | 263 | *(uint32_t *)(result + ptr) = (uint32_t)strlen(meta->name); 264 | ptr += sizeof(uint32_t); 265 | strcpy(result + ptr, meta->name); 266 | ptr += strlen(meta->name) + 1; 267 | 268 | *(uint32_t *)(result + ptr) = (uint32_t)strlen(meta->start_symbol); 269 | ptr += sizeof(uint32_t); 270 | strcpy(result + ptr, meta->start_symbol); 271 | ptr += strlen(meta->start_symbol) + 1; 272 | 273 | *(uint32_t *)(result + ptr) = (uint32_t)strlen(meta->end_symbol); 274 | ptr += sizeof(uint32_t); 275 | strcpy(result + ptr, meta->end_symbol); 276 | ptr += strlen(meta->end_symbol) + 1; 277 | } 278 | return result; 279 | } 280 | -------------------------------------------------------------------------------- /common/hashtable.c: -------------------------------------------------------------------------------- 1 | #include "hashtable.h" 2 | #include 3 | 4 | hashtable_t *create_hashtable(int buckets, unsigned int (*hash_function)(void *)) { 5 | hashtable_t *table = malloc(sizeof(hashtable_t)); 6 | table->hash = hash_function; 7 | table->bucket_count = buckets; 8 | table->buckets = calloc(buckets, sizeof(hashtable_entry_t)); 9 | return table; 10 | } 11 | 12 | void free_bucket(hashtable_entry_t *bucket) { 13 | if (bucket) { 14 | free_bucket(bucket->next); 15 | free(bucket); 16 | } 17 | } 18 | 19 | void free_hashtable(hashtable_t *table) { 20 | int i; 21 | for (i = 0; i < table->bucket_count; ++i) { 22 | free_bucket(table->buckets[i]); 23 | } 24 | free(table); 25 | } 26 | 27 | void *hashtable_get(hashtable_t *table, void *key) { 28 | unsigned int hash = table->hash(key); 29 | unsigned int bucket = hash % table->bucket_count; 30 | hashtable_entry_t *entry = table->buckets[bucket]; 31 | if (entry) { 32 | if (entry->key != hash) { 33 | while (entry->next) { 34 | entry = entry->next; 35 | if (!entry || entry->key == hash) { 36 | break; 37 | } 38 | } 39 | } 40 | } 41 | return entry->value; 42 | } 43 | 44 | void *hashtable_set(hashtable_t *table, void *key, void *value) { 45 | unsigned int hash = table->hash(key); 46 | unsigned int bucket = hash % table->bucket_count; 47 | hashtable_entry_t *entry = table->buckets[bucket]; 48 | hashtable_entry_t *previous = NULL; 49 | if (entry) { 50 | if (entry->key != hash) { 51 | while (entry->next) { 52 | previous = entry; 53 | entry = entry->next; 54 | if (!entry || entry->key == hash) { 55 | break; 56 | } 57 | } 58 | } 59 | } 60 | if (entry == NULL) { 61 | entry = calloc(1, sizeof(hashtable_entry_t)); 62 | entry->key = hash; 63 | table->buckets[bucket] = entry; 64 | if (previous) { 65 | previous->next = entry; 66 | } 67 | } 68 | void *old = entry->value; 69 | entry->value = value; 70 | return old; 71 | } 72 | -------------------------------------------------------------------------------- /common/instructions.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "log.h" 7 | #include "list.h" 8 | #include "instructions.h" 9 | #include "readline.h" 10 | #include "stringop.h" 11 | #include "stack.h" 12 | #include "expression.h" 13 | #include "objects.h" 14 | #include "linker.h" 15 | #include "runtime.h" 16 | 17 | #include "z80.h" 18 | #include "amd64.h" 19 | #include "arm64.h" 20 | 21 | static uint64_t swapbits(uint64_t p, uint64_t m, int k) { 22 | uint64_t q = ((p>>k)^p)&m; 23 | return p^q^(q<>1)&m0) | (n&m0)<<1; 32 | n = swapbits(n, m1, 4); 33 | n = swapbits(n, m2, 8); 34 | n = swapbits(n, m3, 20); 35 | n = (n >> 34) | (n << 30); 36 | return n; 37 | } 38 | 39 | operand_group_t *find_operand_group(instruction_set_t *set, const char *name) { 40 | for (unsigned int i = 0; i < set->operand_groups->length; ++i) { 41 | operand_group_t *g = set->operand_groups->items[i]; 42 | if (strcmp(g->name, name) == 0) { 43 | return g; 44 | } 45 | } 46 | return NULL; 47 | } 48 | 49 | operand_t *find_operand(operand_group_t *group, const char *match) { 50 | for (unsigned int i = 0; i < group->operands->length; ++i) { 51 | operand_t *o = group->operands->items[i]; 52 | if (strcasecmp(o->match, match) == 0) { 53 | return o; 54 | } 55 | } 56 | return NULL; 57 | } 58 | 59 | instruction_operand_t *find_instruction_operand(instruction_t *inst, char key) { 60 | for (unsigned int i = 0; i < inst->operands->length; ++i) { 61 | instruction_operand_t *op = inst->operands->items[i]; 62 | if (op->key == key) { 63 | return op; 64 | } 65 | } 66 | return NULL; 67 | } 68 | 69 | immediate_t *find_instruction_immediate(instruction_t *inst, char key) { 70 | for (unsigned int i = 0; i < inst->immediate->length; ++i) { 71 | immediate_t *imm = inst->immediate->items[i]; 72 | if (imm->ref == key) { 73 | return imm; 74 | } 75 | } 76 | return NULL; 77 | } 78 | 79 | operand_group_t *create_operand_group(const char *name) { 80 | operand_group_t *g = malloc(sizeof(operand_group_t)); 81 | g->name = malloc(strlen(name) + 1); 82 | strcpy(g->name, name); 83 | g->operands = create_list(); 84 | return g; 85 | } 86 | 87 | operand_t *create_operand(const char *match, uint64_t val, size_t len) { 88 | operand_t *op = malloc(sizeof(operand_t)); 89 | op->match = malloc(strlen(match) + 1); 90 | strcpy(op->match, match); 91 | op->value = val; 92 | op->width = len; 93 | return op; 94 | } 95 | 96 | bool parse_operand_line(const char *line, instruction_set_t *set) { 97 | list_t *parts = split_string(line, " \t"); 98 | if (parts->length != 4) { 99 | fprintf(stderr, "Invalid definition found in instruction set: %s\n", line); 100 | free_flat_list(parts); 101 | return false; 102 | } 103 | operand_group_t *g = find_operand_group(set, (char *)parts->items[1]); 104 | if (g == NULL) { 105 | g = create_operand_group((char *)parts->items[1]); 106 | list_add(set->operand_groups, g); 107 | } 108 | char *end; 109 | uint64_t val = (uint64_t)strtol((char *)parts->items[3], &end, 2); 110 | if (*end != '\0') { 111 | fprintf(stderr, "Invalid definition found in instruction set: %s\n", line); 112 | free_flat_list(parts); 113 | return false; 114 | } 115 | operand_t *op = create_operand((char *)parts->items[2], val, strlen((char *)parts->items[3])); 116 | list_add(g->operands, op); 117 | free_flat_list(parts); 118 | return true; 119 | } 120 | 121 | bool parse_instruction_line(const char *line, instruction_set_t *set) { 122 | list_t *parts = split_string(line, " \t"); 123 | if (parts->length <= 2) { 124 | fprintf(stderr, "Invalid definition found in instruction set: %s\n", line); 125 | free_flat_list(parts); 126 | return false; 127 | } 128 | /* Initialize an empty instruction */ 129 | instruction_t *inst = malloc(sizeof(instruction_t)); 130 | inst->match = malloc(strlen(parts->items[1]) + 1); 131 | strcpy(inst->match, parts->items[1]); 132 | inst->operands = create_list(); 133 | inst->immediate = create_list(); 134 | inst->value = 0; 135 | /* Parse match */ 136 | for (size_t i = 0; i < strlen(inst->match); ++i) { 137 | if (inst->match[i] == '@') /* Operand */ { 138 | char key = inst->match[++i]; 139 | i += 2; /* Skip key, < */ 140 | int g_len = strchr(inst->match + i, '>') - (inst->match + i); 141 | char *g = malloc(g_len + 1); 142 | int j; 143 | for (j = 0; inst->match[i] != '>'; ++j) { 144 | g[j] = inst->match[i++]; 145 | } 146 | g[j] = '\0'; 147 | instruction_operand_t *op = malloc(sizeof(instruction_operand_t)); 148 | op->key = key; 149 | op->group = g; 150 | operand_group_t *group = find_operand_group(set, g); 151 | op->width = ((operand_t *)group->operands->items[0])->width; 152 | /* Note: operand shift is not populated yet */ 153 | list_add(inst->operands, op); 154 | } else if (inst->match[i] == '%' || inst->match[i] == '^' || inst->match[i] == '&') /* Immediate value */ { 155 | char type = inst->match[i]; 156 | char key = inst->match[++i]; 157 | i += 2; /* Skip key, < */ 158 | size_t width; 159 | if (type != '&') { 160 | int g_len = strchr(inst->match + i, '>') - (inst->match + i); 161 | char *g = malloc(g_len + 1); 162 | int j; 163 | for (j = 0; inst->match[i] != '>'; ++j) { 164 | g[j] = inst->match[i++]; 165 | } 166 | g[j] = '\0'; 167 | width = atoi(g); 168 | free(g); 169 | } else { 170 | width = 3; 171 | } 172 | immediate_t *imm = malloc(sizeof(immediate_t)); 173 | imm->ref = key; 174 | imm->width = width; 175 | switch (type) { 176 | case '%': 177 | imm->type = IMM_TYPE_ABSOLUTE; 178 | break; 179 | case '^': 180 | imm->type = IMM_TYPE_RELATIVE; 181 | break; 182 | case '&': 183 | imm->type = IMM_TYPE_RESTART; 184 | break; 185 | } 186 | /* Note: immediate value shift is not populated yet */ 187 | list_add(inst->immediate, imm); 188 | } 189 | } 190 | /* Parse value */ 191 | char *_value = malloc(strlen(line) + 1); 192 | strcpy(_value, line); 193 | int trimmed_start; 194 | _value = strip_whitespace(_value, &trimmed_start); 195 | char *value = _value; 196 | while (*value++ != ' ') { } 197 | while (*value++ != ' ') { } 198 | inst->width = 0; 199 | int shift = 0; 200 | for (size_t i = 0; i < strlen(value); ++i) { 201 | if (value[i] == ' ' || value[i] == '\t') { 202 | continue; 203 | } 204 | if (value[i] == '1') { 205 | inst->value |= 1 << shift; 206 | ++inst->width; 207 | ++shift; 208 | } else if (value[i] == '0') { 209 | ++inst->width; 210 | ++shift; 211 | } else if (value[i] == '@') /* Operand */ { 212 | char key = value[++i]; 213 | instruction_operand_t *op = find_instruction_operand(inst, key); 214 | if (op == NULL) { 215 | fprintf(stderr, "Invalid definition found in instruction set (unknown operand group): %s\n", line); 216 | free(_value); 217 | free_flat_list(parts); 218 | return false; 219 | } 220 | op->shift = shift; 221 | shift += op->width; 222 | inst->width += op->width; 223 | } else if (value[i] == '%' || value[i] == '^' || value[i] == '&') /* Immediate value */ { 224 | char key = value[++i]; 225 | immediate_t *imm = find_instruction_immediate(inst, key); 226 | if (imm == NULL) { 227 | fprintf(stderr, "Invalid definition found in instruction set (unknown immediate value): %s\n", line); 228 | free(_value); 229 | free_flat_list(parts); 230 | return false; 231 | } 232 | imm->shift = shift; 233 | shift += imm->width; 234 | inst->width += imm->width; 235 | } 236 | } 237 | inst->value = bitreverse(inst->value) >> (64 - inst->width); 238 | list_add(set->instructions, inst); 239 | free(_value); 240 | free_flat_list(parts); 241 | return true; 242 | } 243 | 244 | bool handle_line(char *line, instruction_set_t *result) { 245 | int trimmed_start; 246 | line = strip_whitespace(line, &trimmed_start); 247 | if (line[0] == '\0' || line[0] == '#') { 248 | free(line); 249 | return true; 250 | } 251 | if (strstr(line, "ARCH ") == line) { 252 | if (result->arch) { 253 | fprintf(stderr, "Instruction set definition contains two ARCH directives!\n\tAttempted to override '%s' with '%s'\n", result->arch, line + 5); 254 | free(line); 255 | return false; 256 | } 257 | else { 258 | result->arch = strdup(line + 5); 259 | free(line); 260 | return true; 261 | } 262 | } 263 | if (strstr(line, "OPERAND ") == line) { 264 | const bool val = parse_operand_line(line, result); 265 | free(line); 266 | return val; 267 | } 268 | if (strstr(line, "INS ") == line) { 269 | const bool val = parse_instruction_line(line, result); 270 | free(line); 271 | return val; 272 | } 273 | fprintf(stderr, "Unrecognized line: %s\n", line); 274 | free(line); 275 | return false; 276 | } 277 | 278 | instruction_set_t *load_instruction_set(FILE *file) { 279 | instruction_set_t *result = malloc(sizeof(instruction_set_t)); 280 | result->instructions = create_list(); 281 | result->operand_groups = create_list(); 282 | result->arch = NULL; 283 | while (!feof(file)) { 284 | char *line = read_line(file); 285 | if (!handle_line(line, result)) { 286 | fprintf(stderr, "Error loading instruction set!\n"); 287 | instruction_set_free(result); 288 | return NULL; 289 | } 290 | } 291 | return result; 292 | } 293 | 294 | instruction_set_t *load_instruction_set_s(const char *set) { 295 | instruction_set_t *result = malloc(sizeof(instruction_set_t)); 296 | result->instructions = create_list(); 297 | result->operand_groups = create_list(); 298 | result->arch = NULL; 299 | int offset = 0; 300 | while (set[offset]) { 301 | char *line = read_line_s(set, &offset); 302 | if (!handle_line(line, result)) { 303 | fprintf(stderr, "Error loading instruction set!\n"); 304 | instruction_set_free(result); 305 | return NULL; 306 | } 307 | } 308 | return result; 309 | } 310 | 311 | void instruction_set_free(instruction_set_t *set) { 312 | unsigned int i, n; 313 | for (i = 0; i < set->instructions->length; ++i) { 314 | instruction_t *inst = set->instructions->items[i]; 315 | for (unsigned int i = 0; i < inst->operands->length; i += 1) { 316 | instruction_operand_t *op = (instruction_operand_t*)inst->operands->items[i]; 317 | free(op->group); 318 | } 319 | free_flat_list(inst->operands); 320 | free_flat_list(inst->immediate); 321 | free(inst->match); 322 | free(inst); 323 | } 324 | list_free(set->instructions); 325 | 326 | for (i = 0; i < set->operand_groups->length; ++i) { 327 | operand_group_t *group = set->operand_groups->items[i]; 328 | for (n = 0; n < group->operands->length; ++n) { 329 | operand_t *op = group->operands->items[n]; 330 | free(op->match); 331 | } 332 | free_flat_list(group->operands); 333 | free(group->name); 334 | free(group); 335 | } 336 | list_free(set->operand_groups); 337 | 338 | if (set->arch != NULL) { 339 | free(set->arch); 340 | } 341 | free(set); 342 | } 343 | 344 | instruction_set_t * 345 | find_instruction_set(void) 346 | { 347 | const char *sets_dir = "/usr/local/share/knightos/scas/"; 348 | const char *ext = ".tab"; 349 | FILE *f = fopen(scas_runtime.arch, "r"); 350 | if(f == NULL){ 351 | char *path = malloc(strlen(scas_runtime.arch) + strlen(sets_dir) + strlen(ext) + 1); 352 | sprintf(path, "%s%s%s", sets_dir, scas_runtime.arch, ext); 353 | f = fopen(path, "r"); 354 | free(path); 355 | if(f == NULL){ 356 | // Fall back to internal copy if recognized 357 | if(strcmp(scas_runtime.arch, "z80") == 0) 358 | return load_instruction_set_s(z80_tab); 359 | if(strcmp(scas_runtime.arch, "arm64") == 0) 360 | return load_instruction_set_s(arm64_tab); 361 | if(strcmp(scas_runtime.arch, "amd64") == 0) 362 | return load_instruction_set_s(amd64_tab); 363 | scas_log(L_ERROR, "Unknown architecture: %s", scas_runtime.arch); 364 | return NULL; 365 | } 366 | } 367 | instruction_set_t *set = load_instruction_set(f); 368 | fclose(f); 369 | return set; 370 | } 371 | 372 | -------------------------------------------------------------------------------- /common/list.c: -------------------------------------------------------------------------------- 1 | #include "list.h" 2 | #include 3 | #include 4 | #include 5 | 6 | list_t *create_list(void) { 7 | list_t *list = malloc(sizeof(list_t)); 8 | list->capacity = 10; 9 | list->length = 0; 10 | list->items = malloc(sizeof(void*) * list->capacity); 11 | return list; 12 | } 13 | 14 | static void list_resize(list_t *list) { 15 | if (list->length == list->capacity) { 16 | list->capacity = list->capacity * 2 + 8; 17 | list->items = realloc(list->items, sizeof(void*) * list->capacity); 18 | } 19 | } 20 | 21 | void list_free(list_t *list) { 22 | if (list == NULL) { 23 | return; 24 | } 25 | free(list->items); 26 | free(list); 27 | } 28 | 29 | void list_foreach(list_t *list, void (*callback)(void *item)) { 30 | if (list == NULL || callback == NULL) { 31 | return; 32 | } 33 | for (unsigned int i = 0; i < list->length; i++) { 34 | callback(list->items[i]); 35 | } 36 | } 37 | 38 | void list_add(list_t *list, void *item) { 39 | list_resize(list); 40 | list->items[list->length++] = item; 41 | } 42 | 43 | void list_insert(list_t *list, unsigned int index, void *item) { 44 | list_resize(list); 45 | memmove(&list->items[index + 1], &list->items[index], sizeof(void*) * (list->length - index)); 46 | list->length++; 47 | list->items[index] = item; 48 | } 49 | 50 | void list_del(list_t *list, unsigned int index) { 51 | list->length--; 52 | if (index != list->length) { 53 | memmove(&list->items[index], &list->items[index + 1], sizeof(void*) * (list->length - index)); 54 | } 55 | } 56 | 57 | void list_cat(list_t *list, list_t *source) { 58 | for (unsigned int i = 0; i < source->length; ++i) { 59 | list_add(list, source->items[i]); 60 | } 61 | } 62 | 63 | void list_qsort(list_t* list, int compare(const void *left, const void *right)) { 64 | qsort(list->items, list->length, sizeof(void *), compare); 65 | } 66 | 67 | int list_seq_find(list_t *list, int compare(const void *item, const void *data), const void *data) { 68 | for (unsigned int i = 0; i < list->length; i++) { 69 | void *item = list->items[i]; 70 | if (compare(item, data) == 0) { 71 | return i; 72 | } 73 | } 74 | return -1; 75 | } 76 | -------------------------------------------------------------------------------- /common/log.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "log.h" 6 | 7 | #ifdef EMSCRIPTEN 8 | #include 9 | #endif 10 | 11 | void scas_log_default(const char *msg) { 12 | fprintf(stderr, "%s", msg); 13 | } 14 | 15 | scas_log_importance_t scas_log_verbosity = 0; 16 | unsigned scas_log_indent = 0; 17 | 18 | bool scas_log_colorize = false; 19 | 20 | void (*scas_log_function)(const char *) = scas_log_default; 21 | 22 | const char *verbosity_colors[] = { 23 | "", // L_SILENT 24 | "\x1B[1;31m", // L_ERROR 25 | "\x1B[1;34m", // L_INFO 26 | "\x1B[1;30m", // L_DEBUG 27 | }; 28 | 29 | void scas_log(scas_log_importance_t verbosity, char* format, ...) { 30 | if (scas_log_function) { 31 | if (verbosity <= scas_log_verbosity && verbosity >= 0) { 32 | size_t c = verbosity; 33 | if (c > sizeof(verbosity_colors) / sizeof(char *)) { 34 | c = sizeof(verbosity_colors) / sizeof(char *) - 1; 35 | } 36 | if (scas_log_colorize) { 37 | scas_log_function(verbosity_colors[c]); 38 | } 39 | if (verbosity == L_DEBUG || verbosity == L_INFO) { 40 | for (unsigned i = 0; i < scas_log_indent; ++i) { 41 | scas_log_function(" "); 42 | } 43 | } 44 | va_list args; 45 | va_start(args, format); 46 | int length = vsnprintf(NULL, 0, format, args); 47 | va_end(args); 48 | if (length > 0) { 49 | va_start(args, format); 50 | length += 1; 51 | char *buf = malloc(length); 52 | vsnprintf(buf, length, format, args); 53 | va_end(args); 54 | scas_log_function(buf); 55 | free(buf); 56 | scas_log_function(scas_log_colorize ? "\n\x1B[0m" : "\n"); 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /common/match.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "list.h" 8 | #include "stringop.h" 9 | #include "instructions.h" 10 | #include "match.h" 11 | #include "log.h" 12 | 13 | /* 14 | * Used to get the value provided in code for an operand or immediate value 15 | * For example, given this match: 16 | * LD_A-,-(-%A<16>-) 17 | * And given this code: 18 | * ld a, (0x100 + 2) 19 | * get_operand_string could be used to extract "a" and "0x100 + 2" 20 | */ 21 | char *get_operand_string(instruction_t *inst, unsigned int *i, const char *code, int j) { 22 | char delimiter = '\0'; 23 | char *res; 24 | while (inst->match[*i]) { 25 | if (inst->match[*i] == '-' || inst->match[*i] == '_') { 26 | ++*i; 27 | } else { 28 | delimiter = inst->match[*i]; 29 | break; 30 | } 31 | } 32 | if (delimiter == '\0') { 33 | return strdup(code + j); 34 | } 35 | const char *significant_delimiters = "%@&^"; 36 | if (strchr(significant_delimiters, delimiter)) { 37 | delimiter = '*'; 38 | } 39 | char *end; 40 | if (delimiter == '*') { 41 | const char *toks = "+- \t"; // Valid delimiters in this scenario 42 | for (size_t k = 0; k < strlen(toks); ++k) { 43 | end = code_strchr(code + j, toks[k]); 44 | if (end) break; 45 | } 46 | } else { 47 | end = code_strchr(code + j, delimiter); 48 | if (end == NULL && delimiter == ')') { 49 | end = code_strchr(code + j, ']'); 50 | } 51 | } 52 | if (end == NULL) { 53 | return NULL; 54 | } 55 | res = malloc(end - (code + j) + 1); 56 | strncpy(res, code + j, end - (code + j)); 57 | res[end - (code + j)] = '\0'; 58 | int _; 59 | res = strip_whitespace(res, &_); 60 | --*i; 61 | return res; 62 | } 63 | 64 | void match_free(instruction_match_t *match) { 65 | for (unsigned int i = 0; i < match->immediate_values->length; i += 1) { 66 | immediate_ref_t *ref = (immediate_ref_t*)match->immediate_values->items[i]; 67 | free(ref->value_provided); 68 | free(ref); 69 | } 70 | list_free(match->immediate_values); 71 | free_flat_list(match->operands); 72 | free(match); 73 | } 74 | 75 | instruction_match_t *try_match(instruction_set_t *set, instruction_t *inst, const char *str) { 76 | instruction_match_t *result = malloc(sizeof(instruction_match_t)); 77 | result->operands = create_list(); 78 | result->immediate_values = create_list(); 79 | result->instruction = inst; 80 | 81 | unsigned int i, j; 82 | int whitespace_met = 0; 83 | int match = 1; 84 | for (i = 0, j = 0; inst->match[i] && str[j]; ++i, ++j) { 85 | if (inst->match[i] == '_') /* Required whitespace */ { 86 | if (whitespace_met && isspace(str[j])) { 87 | /* Current character is whitespace and we have met the whitespace requirement */ 88 | i--; 89 | } else if (whitespace_met && !isspace(str[j])) { 90 | /* Current character is not whitespace and we have met the whitespace requirement */ 91 | j--; 92 | whitespace_met = 0; 93 | } else { 94 | /* We have not met the required whitespace yet */ 95 | if (!isspace(str[j])) { 96 | /* We have not met the required whitespace, and this is not whitespace */ 97 | match = 0; 98 | break; 99 | } else { 100 | /* We have not met the required whitespace, and this is whitespace */ 101 | i--; 102 | whitespace_met = 1; 103 | } 104 | } 105 | } else if (inst->match[i] == '-') /* Optional whitespace */ { 106 | if (isspace(str[j])) { 107 | i--; 108 | } else { 109 | j--; 110 | } 111 | } else if (inst->match[i] == '%' || inst->match[i] == '^' || inst->match[i] == '&') /* Immediate value */ { 112 | char type = inst->match[i]; 113 | char key = inst->match[++i]; 114 | if (type != '&') { 115 | while (inst->match[++i] != '>') { } 116 | ++i; 117 | } else { 118 | ++i; 119 | } 120 | char *value = get_operand_string(inst, &i, str, j); 121 | if (value == NULL) { 122 | match = 0; 123 | break; 124 | } 125 | j += strlen(value) - 1; 126 | immediate_ref_t *ref = malloc(sizeof(immediate_ref_t)); 127 | ref->key = key; 128 | ref->value_provided = value; 129 | list_add(result->immediate_values, ref); 130 | i--; 131 | } else if (inst->match[i] == '@') /* Operand */ { 132 | char key = inst->match[++i]; i += 2; 133 | char *start = inst->match + i; 134 | int g_len = 0; 135 | while (inst->match[i] != '>') { 136 | g_len++; i++; 137 | } 138 | ++i; 139 | char *group_name = malloc(g_len + 1); 140 | strncpy(group_name, start, g_len); 141 | group_name[g_len] = 0; 142 | operand_group_t *group = find_operand_group(set, group_name); 143 | free(group_name); 144 | if (group == NULL) { 145 | match = 0; 146 | break; 147 | } 148 | char *value = get_operand_string(inst, &i, str, j); 149 | if (value == NULL) { 150 | match = 0; 151 | break; 152 | } 153 | operand_t *op = find_operand(group, value); 154 | if (op == NULL) { 155 | match = 0; 156 | free(value); 157 | break; 158 | } 159 | instruction_operand_t *inst_op = find_instruction_operand(inst, key); 160 | operand_ref_t *ref = malloc(sizeof(operand_ref_t)); 161 | ref->shift = inst_op->shift; 162 | ref->op = op; 163 | list_add(result->operands, ref); 164 | j += strlen(value) - 1; 165 | free(value); 166 | i--; 167 | } else { 168 | if (inst->match[i] == '(' && str[j] == '[') { 169 | scas_log(L_DEBUG, "Permitting [ in place of ("); 170 | continue; 171 | } 172 | if (inst->match[i] == ')' && str[j] == ']') { 173 | scas_log(L_DEBUG, "Permitting ] in place of )"); 174 | continue; 175 | } 176 | if (toupper(inst->match[i]) != toupper(str[j])) { 177 | match = 0; 178 | break; 179 | } 180 | } 181 | } 182 | if (str[j] || inst->match[i] || !match) { 183 | /* Not a match, clean up */ 184 | free_flat_list(result->operands); 185 | for (i = 0; i < result->immediate_values->length; ++i) { 186 | immediate_ref_t *ref = result->immediate_values->items[i]; 187 | free(ref->value_provided); 188 | free(ref); 189 | } 190 | list_free(result->immediate_values); 191 | free(result); 192 | return NULL; 193 | } 194 | return result; 195 | } 196 | 197 | instruction_match_t *match_instruction(instruction_set_t *set, const char *str) { 198 | for (unsigned int i = 0; i < set->instructions->length; ++i) { 199 | instruction_t *inst = set->instructions->items[i]; 200 | instruction_match_t *match = try_match(set, inst, str); 201 | if (match) { 202 | return match; 203 | } 204 | } 205 | return NULL; 206 | } 207 | -------------------------------------------------------------------------------- /common/md5.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. 3 | * MD5 Message-Digest Algorithm (RFC 1321). 4 | * 5 | * Homepage: 6 | * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 7 | * 8 | * Author: 9 | * Alexander Peslyak, better known as Solar Designer 10 | * 11 | * This software was written by Alexander Peslyak in 2001. No copyright is 12 | * claimed, and the software is hereby placed in the public domain. 13 | * In case this attempt to disclaim copyright and place the software in the 14 | * public domain is deemed null and void, then the software is 15 | * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the 16 | * general public under the following terms: 17 | * 18 | * Redistribution and use in source and binary forms, with or without 19 | * modification, are permitted. 20 | * 21 | * There's ABSOLUTELY NO WARRANTY, express or implied. 22 | * 23 | * (This is a heavily cut-down "BSD license".) 24 | * 25 | * This differs from Colin Plumb's older public domain implementation in that 26 | * no exactly 32-bit integer data type is required (any 32-bit or wider 27 | * unsigned integer data type will do), there's no compile-time endianness 28 | * configuration, and the function prototypes match OpenSSL's. No code from 29 | * Colin Plumb's implementation has been reused; this comment merely compares 30 | * the properties of the two independent implementations. 31 | * 32 | * The primary goals of this implementation are portability and ease of use. 33 | * It is meant to be fast, but not as fast as possible. Some known 34 | * optimizations are not included to reduce source code size and avoid 35 | * compile-time configuration. 36 | */ 37 | 38 | #include 39 | 40 | #include "md5.h" 41 | 42 | /* 43 | * The basic MD5 functions. 44 | * 45 | * F and G are optimized compared to their RFC 1321 definitions for 46 | * architectures that lack an AND-NOT instruction, just like in Colin Plumb's 47 | * implementation. 48 | */ 49 | #define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) 50 | #define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) 51 | #define H(x, y, z) (((x) ^ (y)) ^ (z)) 52 | #define H2(x, y, z) ((x) ^ ((y) ^ (z))) 53 | #define I(x, y, z) ((y) ^ ((x) | ~(z))) 54 | 55 | /* 56 | * The MD5 transformation for all four rounds. 57 | */ 58 | #define STEP(f, a, b, c, d, x, t, s) \ 59 | (a) += f((b), (c), (d)) + (x) + (t); \ 60 | (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ 61 | (a) += (b); 62 | 63 | /* 64 | * SET reads 4 input bytes in little-endian byte order and stores them 65 | * in a properly aligned word in host byte order. 66 | */ 67 | #define SET(n) \ 68 | (ctx->block[(n)] = \ 69 | (MD5_u32plus)ptr[(n) * 4] | \ 70 | ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \ 71 | ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \ 72 | ((MD5_u32plus)ptr[(n) * 4 + 3] << 24)) 73 | #define GET(n) \ 74 | (ctx->block[(n)]) 75 | 76 | /* 77 | * This processes one or more 64-byte data blocks, but does NOT update 78 | * the bit counters. There are no alignment requirements. 79 | */ 80 | static const void *body(MD5_CTX *ctx, const void *data, unsigned long size) 81 | { 82 | const unsigned char *ptr; 83 | MD5_u32plus a, b, c, d; 84 | MD5_u32plus saved_a, saved_b, saved_c, saved_d; 85 | 86 | ptr = (const unsigned char *)data; 87 | 88 | a = ctx->a; 89 | b = ctx->b; 90 | c = ctx->c; 91 | d = ctx->d; 92 | 93 | do { 94 | saved_a = a; 95 | saved_b = b; 96 | saved_c = c; 97 | saved_d = d; 98 | 99 | /* Round 1 */ 100 | STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7) 101 | STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12) 102 | STEP(F, c, d, a, b, SET(2), 0x242070db, 17) 103 | STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22) 104 | STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7) 105 | STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12) 106 | STEP(F, c, d, a, b, SET(6), 0xa8304613, 17) 107 | STEP(F, b, c, d, a, SET(7), 0xfd469501, 22) 108 | STEP(F, a, b, c, d, SET(8), 0x698098d8, 7) 109 | STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12) 110 | STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17) 111 | STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22) 112 | STEP(F, a, b, c, d, SET(12), 0x6b901122, 7) 113 | STEP(F, d, a, b, c, SET(13), 0xfd987193, 12) 114 | STEP(F, c, d, a, b, SET(14), 0xa679438e, 17) 115 | STEP(F, b, c, d, a, SET(15), 0x49b40821, 22) 116 | 117 | /* Round 2 */ 118 | STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5) 119 | STEP(G, d, a, b, c, GET(6), 0xc040b340, 9) 120 | STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14) 121 | STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20) 122 | STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5) 123 | STEP(G, d, a, b, c, GET(10), 0x02441453, 9) 124 | STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14) 125 | STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20) 126 | STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5) 127 | STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9) 128 | STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14) 129 | STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20) 130 | STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5) 131 | STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9) 132 | STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14) 133 | STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20) 134 | 135 | /* Round 3 */ 136 | STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4) 137 | STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11) 138 | STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16) 139 | STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23) 140 | STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4) 141 | STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11) 142 | STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16) 143 | STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23) 144 | STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4) 145 | STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11) 146 | STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16) 147 | STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23) 148 | STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4) 149 | STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11) 150 | STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16) 151 | STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23) 152 | 153 | /* Round 4 */ 154 | STEP(I, a, b, c, d, GET(0), 0xf4292244, 6) 155 | STEP(I, d, a, b, c, GET(7), 0x432aff97, 10) 156 | STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15) 157 | STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21) 158 | STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6) 159 | STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10) 160 | STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15) 161 | STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21) 162 | STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6) 163 | STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10) 164 | STEP(I, c, d, a, b, GET(6), 0xa3014314, 15) 165 | STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21) 166 | STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6) 167 | STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10) 168 | STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15) 169 | STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21) 170 | 171 | a += saved_a; 172 | b += saved_b; 173 | c += saved_c; 174 | d += saved_d; 175 | 176 | ptr += 64; 177 | } while (size -= 64); 178 | 179 | ctx->a = a; 180 | ctx->b = b; 181 | ctx->c = c; 182 | ctx->d = d; 183 | 184 | return ptr; 185 | } 186 | 187 | void MD5_Init(MD5_CTX *ctx) 188 | { 189 | ctx->a = 0x67452301; 190 | ctx->b = 0xefcdab89; 191 | ctx->c = 0x98badcfe; 192 | ctx->d = 0x10325476; 193 | 194 | ctx->lo = 0; 195 | ctx->hi = 0; 196 | } 197 | 198 | void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size) 199 | { 200 | MD5_u32plus saved_lo; 201 | unsigned long used, available; 202 | 203 | saved_lo = ctx->lo; 204 | if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) 205 | ctx->hi++; 206 | ctx->hi += size >> 29; 207 | 208 | used = saved_lo & 0x3f; 209 | 210 | if (used) { 211 | available = 64 - used; 212 | 213 | if (size < available) { 214 | memcpy(&ctx->buffer[used], data, size); 215 | return; 216 | } 217 | 218 | memcpy(&ctx->buffer[used], data, available); 219 | data = (const unsigned char *)data + available; 220 | size -= available; 221 | body(ctx, ctx->buffer, 64); 222 | } 223 | 224 | if (size >= 64) { 225 | data = body(ctx, data, size & ~(unsigned long)0x3f); 226 | size &= 0x3f; 227 | } 228 | 229 | memcpy(ctx->buffer, data, size); 230 | } 231 | 232 | void MD5_Final(unsigned char *result, MD5_CTX *ctx) 233 | { 234 | unsigned long used, available; 235 | 236 | used = ctx->lo & 0x3f; 237 | 238 | ctx->buffer[used++] = 0x80; 239 | 240 | available = 64 - used; 241 | 242 | if (available < 8) { 243 | memset(&ctx->buffer[used], 0, available); 244 | body(ctx, ctx->buffer, 64); 245 | used = 0; 246 | available = 64; 247 | } 248 | 249 | memset(&ctx->buffer[used], 0, available - 8); 250 | 251 | ctx->lo <<= 3; 252 | ctx->buffer[56] = ctx->lo; 253 | ctx->buffer[57] = ctx->lo >> 8; 254 | ctx->buffer[58] = ctx->lo >> 16; 255 | ctx->buffer[59] = ctx->lo >> 24; 256 | ctx->buffer[60] = ctx->hi; 257 | ctx->buffer[61] = ctx->hi >> 8; 258 | ctx->buffer[62] = ctx->hi >> 16; 259 | ctx->buffer[63] = ctx->hi >> 24; 260 | 261 | body(ctx, ctx->buffer, 64); 262 | 263 | result[0] = ctx->a; 264 | result[1] = ctx->a >> 8; 265 | result[2] = ctx->a >> 16; 266 | result[3] = ctx->a >> 24; 267 | result[4] = ctx->b; 268 | result[5] = ctx->b >> 8; 269 | result[6] = ctx->b >> 16; 270 | result[7] = ctx->b >> 24; 271 | result[8] = ctx->c; 272 | result[9] = ctx->c >> 8; 273 | result[10] = ctx->c >> 16; 274 | result[11] = ctx->c >> 24; 275 | result[12] = ctx->d; 276 | result[13] = ctx->d >> 8; 277 | result[14] = ctx->d >> 16; 278 | result[15] = ctx->d >> 24; 279 | 280 | memset(ctx, 0, sizeof(*ctx)); 281 | } 282 | -------------------------------------------------------------------------------- /common/objects.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "list.h" 8 | #include "stack.h" 9 | #include "expression.h" 10 | #include "objects.h" 11 | #include "log.h" 12 | #include "functions.h" 13 | #include "readline.h" 14 | 15 | #define SCASOBJ_VERSION 2 16 | 17 | object_t *create_object(void) { 18 | object_t *o = malloc(sizeof(object_t)); 19 | o->areas = create_list(); 20 | o->exports = create_list(); 21 | o->imports = create_list(); 22 | o->unresolved = create_list(); 23 | o->merged = false; 24 | return o; 25 | } 26 | 27 | void object_free(object_t *o) { 28 | for (unsigned int i = 0; i < o->areas->length; i += 1) { 29 | area_t *area = (area_t*)o->areas->items[i]; 30 | if (o->merged) { 31 | merged_area_free(area); 32 | } 33 | else { 34 | area_free(area); 35 | } 36 | } 37 | list_free(o->areas); 38 | for (unsigned int i = 0; i < o->unresolved->length; i += 1) { 39 | unresolved_symbol_t *u = (unresolved_symbol_t*)o->unresolved->items[i]; 40 | free(u->name); 41 | free(u->line); 42 | free(u->file_name); 43 | free(u); 44 | } 45 | list_free(o->unresolved); 46 | list_free(o->imports); 47 | list_free(o->exports); 48 | free(o); 49 | } 50 | 51 | area_t *create_area(const char *name) { 52 | area_t *a = malloc(sizeof(area_t)); 53 | a->name = strdup(name); 54 | a->late_immediates = create_list(); 55 | a->symbols = create_list(); 56 | a->source_map = create_list(); 57 | a->metadata = create_list(); 58 | a->final_address = 0; 59 | a->data_length = 0; 60 | a->data_capacity = 1024; 61 | a->data = malloc(a->data_capacity); 62 | return a; 63 | } 64 | 65 | void merged_area_free(area_t *area) { 66 | for (unsigned int i = 0; i < area->metadata->length; ++i) { 67 | metadata_t *meta = area->metadata->items[i]; 68 | free(meta->key); 69 | free(meta->value); 70 | free(meta); 71 | } 72 | list_free(area->metadata); 73 | list_free(area->source_map); 74 | list_free(area->symbols); 75 | list_free(area->late_immediates); 76 | free(area->name); 77 | free(area->data); 78 | free(area); 79 | } 80 | 81 | void area_free(area_t *area) { 82 | for (unsigned int i = 0; i < area->metadata->length; ++i) { 83 | metadata_t *meta = area->metadata->items[i]; 84 | free(meta->key); 85 | free(meta->value); 86 | free(meta); 87 | } 88 | list_free(area->metadata); 89 | for (unsigned int i = 0; i < area->source_map->length; i += 1) { 90 | source_map_free((source_map_t*)area->source_map->items[i]); 91 | } 92 | list_free(area->source_map); 93 | for (unsigned int i = 0; i < area->symbols->length; i += 1) { 94 | symbol_t *sym = (symbol_t*)area->symbols->items[i]; 95 | free(sym->name); 96 | free(sym); 97 | } 98 | list_free(area->symbols); 99 | for (unsigned int i = 0; i < area->late_immediates->length; i += 1) { 100 | late_immediate_t *imm = (late_immediate_t *)area->late_immediates->items[i]; 101 | free_expression(imm->expression); 102 | free(imm); 103 | } 104 | list_free(area->late_immediates); 105 | free(area->name); 106 | free(area->data); 107 | free(area); 108 | } 109 | 110 | metadata_t *get_area_metadata(area_t *area, const char *key) { 111 | for (unsigned int i = 0; i < area->metadata->length; ++i) { 112 | metadata_t *meta = area->metadata->items[i]; 113 | if (strcmp(meta->key, key) == 0) { 114 | return meta; 115 | } 116 | } 117 | return NULL; 118 | } 119 | 120 | void set_area_metadata(area_t *area, const char *key, char *value, uint64_t value_length) { 121 | bool dupe = true; 122 | for (unsigned int i = 0; i < area->metadata->length; ++i) { 123 | metadata_t *meta = area->metadata->items[i]; 124 | if (strcmp(meta->key, key) == 0) { 125 | free(meta->key); 126 | if (meta->value != value) { 127 | free(meta->value); 128 | } 129 | else { 130 | dupe = false; 131 | } 132 | free(meta); 133 | list_del(area->metadata, i); 134 | break; 135 | } 136 | } 137 | metadata_t *newmeta = malloc(sizeof(metadata_t)); 138 | newmeta->key = strdup(key); 139 | newmeta->value_length = value_length; 140 | if (dupe) { 141 | newmeta->value = malloc(value_length); 142 | memcpy(newmeta->value, value, value_length); 143 | } 144 | else { 145 | newmeta->value = value; 146 | } 147 | scas_log(L_DEBUG, "Set area metadata '%s' to new value with length %d", newmeta->key, newmeta->value_length); 148 | list_add(area->metadata, newmeta); 149 | } 150 | 151 | void append_to_area(area_t *area, uint8_t *data, size_t length) { 152 | while ((area->data_capacity - area->data_length) < length) { 153 | /* Expand capacity */ 154 | area->data = realloc(area->data, area->data_capacity + 1024); 155 | area->data_capacity += 1024; 156 | } 157 | memcpy(area->data + area->data_length, data, length); 158 | area->data_length += length; 159 | scas_log(L_DEBUG, "Added %d bytes to area '%s' (now %d bytes total)", length, area->name, area->data_length); 160 | } 161 | 162 | void insert_in_area(area_t *area, uint8_t *data, size_t length, size_t index) { 163 | while (area->data_capacity < length + area->data_length) { 164 | /* Expand capacity */ 165 | area->data = realloc(area->data, area->data_capacity + 1024); 166 | area->data_capacity += 1024; 167 | } 168 | memmove(area->data + index + length, area->data + index, area->data_length - index); 169 | memcpy(area->data + index, data, length); 170 | area->data_length += length; 171 | scas_log(L_DEBUG, "Inserted %d bytes in area '%s' (now %d bytes total)", length, area->name, area->data_length); 172 | } 173 | 174 | void delete_from_area(area_t *area, size_t index, size_t length) { 175 | scas_log(L_DEBUG, "Removing %d bytes at %08X from area '%s'", length, index, area->name); 176 | memmove(area->data + index, area->data + index + length, area->data_length - (index + length)); 177 | area->data_length -= length; 178 | } 179 | 180 | void write_area(FILE *f, area_t *a) { 181 | uint32_t len; 182 | uint64_t len64; 183 | unsigned int i; 184 | fprintf(f, "%s", a->name); fputc(0, f); 185 | /* Symbols */ 186 | fwrite(&a->symbols->length, sizeof(uint32_t), 1, f); 187 | for (i = 0; i < a->symbols->length; ++i) { 188 | symbol_t *sym = a->symbols->items[i]; 189 | fputc(sym->exported, f); 190 | len = strlen(sym->name); 191 | fwrite(&len, sizeof(uint32_t), 1, f); 192 | fprintf(f, "%s", sym->name); 193 | fwrite(&sym->value, sizeof(uint64_t), 1, f); 194 | fwrite(&sym->defined_address, sizeof(uint64_t), 1, f); 195 | } 196 | /* Imports (TODO) */ 197 | /* Expressions */ 198 | fwrite(&a->late_immediates->length, sizeof(uint32_t), 1, f); 199 | for (i = 0; i < a->late_immediates->length; ++i) { 200 | late_immediate_t *imm = a->late_immediates->items[i]; 201 | fputc(imm->type, f); 202 | fputc(imm->width, f); 203 | fwrite(&imm->instruction_address, sizeof(uint64_t), 1, f); 204 | fwrite(&imm->base_address, sizeof(uint64_t), 1, f); 205 | fwrite(&imm->address, sizeof(uint64_t), 1, f); 206 | fwrite_tokens(f, imm->expression); 207 | } 208 | /* Machine code */ 209 | fwrite(&a->data_length, sizeof(uint64_t), 1, f); 210 | fwrite(a->data, sizeof(uint8_t), a->data_length, f); 211 | /* Metadata */ 212 | fwrite(&a->metadata->length, sizeof(uint64_t), 1, f); 213 | for (i = 0; i < a->metadata->length; ++i) { 214 | metadata_t *meta = a->metadata->items[i]; 215 | fputc((uint8_t)strlen(meta->key), f); 216 | fwrite(meta->key, sizeof(char), strlen(meta->key), f); 217 | fwrite(&meta->value_length, sizeof(uint64_t), 1, f); 218 | fwrite(meta->value, sizeof(char), meta->value_length, f); 219 | } 220 | /* Source map */ 221 | fwrite(&a->source_map->length, sizeof(uint64_t), 1, f); 222 | for (i = 0; i < a->source_map->length; ++i) { 223 | source_map_t *map = a->source_map->items[i]; 224 | fwrite(map->file_name, sizeof(char), strlen(map->file_name), f); 225 | fputc(0, f); 226 | len64 = map->entries->length; 227 | fwrite(&len64, sizeof(uint64_t), 1, f); 228 | for (unsigned int j = 0; j < map->entries->length; ++j) { 229 | source_map_entry_t *entry = map->entries->items[j]; 230 | fwrite(&entry->line_number, sizeof(uint64_t), 1, f); 231 | fwrite(&entry->address, sizeof(uint64_t), 1, f); 232 | fwrite(&entry->length, sizeof(uint64_t), 1, f); 233 | fwrite(entry->source_code, sizeof(char), strlen(entry->source_code), f); 234 | fputc(0, f); 235 | } 236 | } 237 | } 238 | 239 | void fwriteobj(FILE *f, object_t *o) { 240 | /* Header */ 241 | fprintf(f, "SCASOBJ"); 242 | fputc(SCASOBJ_VERSION, f); 243 | /* Areas */ 244 | uint32_t a_len = o->areas->length; 245 | fwrite(&a_len, sizeof(uint32_t), 1, f); 246 | for (unsigned int i = 0; i < o->areas->length; ++i) { 247 | area_t *a = o->areas->items[i]; 248 | write_area(f, a); 249 | } 250 | fflush(f); 251 | } 252 | 253 | area_t *read_area(FILE *f) { 254 | char *name = read_line(f); 255 | area_t *area = create_area(name); 256 | scas_log(L_DEBUG, "Reading area '%s' from file", name); 257 | free(name); 258 | uint32_t symbols, immediates; 259 | if (fread(&symbols, 1, sizeof(uint32_t), f) != sizeof(uint32_t)) { 260 | scas_log(L_ERROR, "Failed to read area from file"); 261 | area_free(area); 262 | return NULL; 263 | } 264 | uint32_t len; 265 | for (uint32_t i = 0; i < symbols; ++i) { 266 | symbol_t *sym = malloc(sizeof(symbol_t)); 267 | sym->exported = fgetc(f); 268 | if (fread(&len, sizeof(uint32_t), 1, f) != 1) { 269 | scas_log(L_ERROR, "Failed to read in data!"); 270 | return NULL; 271 | } 272 | sym->name = calloc(len + 1, sizeof(char)); 273 | if (fread(sym->name, 1, len, f) != len) { 274 | scas_log(L_ERROR, "Failed to read in data!"); 275 | return NULL; 276 | } 277 | if (fread(&sym->value, 1, sizeof(uint64_t), f) != sizeof(uint64_t)) { 278 | scas_log(L_ERROR, "Failed to read in data!"); 279 | return NULL; 280 | } 281 | if (fread(&sym->defined_address, 1, sizeof(uint64_t), f) != sizeof(uint64_t)) { 282 | scas_log(L_ERROR, "Failed to read in data!"); 283 | return NULL; 284 | } 285 | sym->type = SYMBOL_LABEL; 286 | list_add(area->symbols, sym); 287 | scas_log(L_DEBUG, "Read symbol '%s' with value 0x%08X%08X", sym->name, (uint32_t)(sym->value >> 32), (uint32_t)sym->value); 288 | } 289 | /* TODO: Imports */ 290 | if (fread(&immediates, 1, sizeof(uint32_t), f) != sizeof(uint32_t)) { 291 | scas_log(L_ERROR, "Failed to read in data!"); 292 | return NULL; 293 | } 294 | for (uint32_t i = 0; i < immediates; ++i) { 295 | late_immediate_t *imm = malloc(sizeof(late_immediate_t)); 296 | imm->type = fgetc(f); 297 | imm->width = fgetc(f); 298 | if (fread(&imm->instruction_address, sizeof(uint64_t), 1, f) != 1) { 299 | scas_log(L_ERROR, "Failed to read in data!"); 300 | return NULL; 301 | } 302 | if (fread(&imm->base_address, sizeof(uint64_t), 1, f) != 1) { 303 | scas_log(L_ERROR, "Failed to read in data!"); 304 | return NULL; 305 | } 306 | if (fread(&imm->address, sizeof(uint64_t), 1, f) != 1) { 307 | scas_log(L_ERROR, "Failed to read in data!"); 308 | return NULL; 309 | } 310 | imm->expression = fread_tokenized_expression(f); 311 | list_add(area->late_immediates, imm); 312 | scas_log(L_DEBUG, "Read immediate value at 0x%08X (width: %d)", imm->address, imm->width); 313 | } 314 | if (fread(&area->data_length, sizeof(uint64_t), 1, f) != 1) { 315 | scas_log(L_ERROR, "Failed to read in data!"); 316 | return NULL; 317 | } 318 | area->data_capacity = area->data_length; 319 | free(area->data); 320 | area->data = malloc(area->data_length); 321 | if (fread(area->data, 1, area->data_length, f) != area->data_length) { 322 | scas_log(L_ERROR, "Failed to read in data!"); 323 | return NULL; 324 | } 325 | scas_log(L_DEBUG, "Read %d bytes of machine code", area->data_length); 326 | 327 | uint64_t meta_length, meta_key; 328 | if (fread(&meta_length, sizeof(uint64_t), 1, f) != 1) { 329 | scas_log(L_ERROR, "Failed to read in data!"); 330 | return NULL; 331 | } 332 | meta_length = (int)meta_length; 333 | scas_log(L_DEBUG, "Reading %d metadata entries", meta_length); 334 | for (uint64_t i = 0; i < meta_length; ++i) { 335 | scas_log(L_DEBUG, "Reading metadata entry %lld of %lld", i, meta_length); 336 | metadata_t *meta = malloc(sizeof(metadata_t)); 337 | meta_key = fgetc(f); 338 | meta->key = malloc(meta_key + 1); 339 | meta->key[meta_key] = 0; 340 | if (fread(meta->key, sizeof(char), meta_key, f) != meta_key) { 341 | scas_log(L_ERROR, "Failed to read in data!"); 342 | return NULL; 343 | } 344 | if (fread(&meta->value_length, sizeof(uint64_t), 1, f) != 1) { 345 | scas_log(L_ERROR, "Failed to read in data!"); 346 | return NULL; 347 | } 348 | meta->value = malloc(meta->value_length + 1); 349 | meta->value[meta->value_length] = 0; 350 | if (fread(meta->value, sizeof(char), meta->value_length, f) != meta->value_length) { 351 | scas_log(L_ERROR, "Failed to read in data!"); 352 | return NULL; 353 | } 354 | list_add(area->metadata, meta); 355 | scas_log(L_DEBUG, "Read metadata %s with value length %d", meta->key, meta->value_length); 356 | } 357 | 358 | uint64_t fileno, lineno; 359 | if (fread(&fileno, sizeof(uint64_t), 1, f) != 1) { 360 | scas_log(L_ERROR, "Failed to read in data!"); 361 | return NULL; 362 | } 363 | fileno = (int)fileno; 364 | for (uint64_t i = 0; i < fileno; ++i) { 365 | source_map_t *map = malloc(sizeof(source_map_t)); 366 | map->file_name = read_line(f); 367 | map->entries = create_list(); 368 | if (fread(&lineno, sizeof(uint64_t), 1, f) != 1) { 369 | scas_log(L_ERROR, "Failed to read in data!"); 370 | return NULL; 371 | } 372 | scas_log(L_DEBUG, "Reading source map for '%s', %d entries", map->file_name, lineno); 373 | for (uint64_t j = 0; j < lineno; ++j) { 374 | source_map_entry_t *entry = malloc(sizeof(source_map_entry_t)); 375 | if (fread(&entry->line_number, sizeof(uint64_t), 1, f) != 1) { 376 | scas_log(L_ERROR, "Failed to read in data!"); 377 | return NULL; 378 | } 379 | if (fread(&entry->address, sizeof(uint64_t), 1, f) != 1) { 380 | scas_log(L_ERROR, "Failed to read in data!"); 381 | return NULL; 382 | } 383 | if (fread(&entry->length, sizeof(uint64_t), 1, f) != 1) { 384 | scas_log(L_ERROR, "Failed to read in data!"); 385 | return NULL; 386 | } 387 | entry->source_code = read_line(f); 388 | list_add(map->entries, entry); 389 | scas_log(L_DEBUG, "Read entry at 0x%08X%08X (line %d): %s", (uint32_t)(entry->address >> 32), (uint32_t)entry->address, entry->line_number, entry->source_code); 390 | } 391 | list_add(area->source_map, map); 392 | } 393 | return area; 394 | } 395 | 396 | object_t *freadobj(FILE *f, const char *name) { 397 | char magic[7]; 398 | int len = fread(magic, sizeof(char), 7, f); 399 | if (len != 7 || strncmp("SCASOBJ", magic, 7) != 0) { 400 | scas_log(L_ERROR, "'%s' is not a valid object file.", name); 401 | return NULL; 402 | } 403 | int ver = fgetc(f); 404 | if (ver != SCASOBJ_VERSION) { 405 | scas_log(L_ERROR, "'%s' was built with an incompatible version of scas.", name); 406 | return NULL; 407 | } 408 | uint32_t area_count; 409 | if (fread(&area_count, sizeof(uint32_t), 1, f) != 1) { 410 | scas_log(L_ERROR, "Failed to read in data!"); 411 | return NULL; 412 | } 413 | object_t *o = create_object(); 414 | for (uint32_t i = 0; i < area_count; ++i) { 415 | list_add(o->areas, read_area(f)); 416 | } 417 | return o; 418 | } 419 | 420 | source_map_t *create_source_map(area_t *area, const char *file_name) { 421 | source_map_t *map = malloc(sizeof(source_map_t)); 422 | map->file_name = strdup(file_name); 423 | map->entries = create_list(); 424 | list_add(area->source_map, map); 425 | return map; 426 | } 427 | 428 | void source_map_free(source_map_t *map) { 429 | for (unsigned int i = 0; i < map->entries->length; i += 1) { 430 | source_map_entry_t *entry = (source_map_entry_t*)map->entries->items[i]; 431 | free(entry->source_code); 432 | free(entry); 433 | } 434 | list_free(map->entries); 435 | free(map->file_name); 436 | free(map); 437 | } 438 | 439 | void add_source_map(source_map_t *map, int line_number, const char *line, uint64_t address, uint64_t length) { 440 | source_map_entry_t *entry = malloc(sizeof(source_map_entry_t)); 441 | entry->line_number = line_number; 442 | entry->address = address; 443 | entry->length = length; 444 | entry->source_code = strdup(line); 445 | list_add(map->entries, entry); 446 | } 447 | -------------------------------------------------------------------------------- /common/operators.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "list.h" 5 | #include "stack.h" 6 | #include "operators.h" 7 | #include "expression.h" 8 | 9 | uint64_t operator_add(stack_type *stack, int *error) { 10 | expression_token_t *right = stack_pop(stack); 11 | expression_token_t *left = stack_pop(stack); 12 | if (left == NULL || right == NULL) { 13 | *error = EXPRESSION_BAD_SYNTAX; 14 | return 0; 15 | } 16 | return left->number + right->number; 17 | } 18 | 19 | uint64_t operator_subtract(stack_type *stack, int *error) { 20 | expression_token_t *right = stack_pop(stack); 21 | expression_token_t *left = stack_pop(stack); 22 | if (left == NULL || right == NULL) { 23 | *error = EXPRESSION_BAD_SYNTAX; 24 | return 0; 25 | } 26 | return left->number - right->number; 27 | } 28 | 29 | uint64_t operator_unary_plus(stack_type *stack, int *error) { 30 | expression_token_t *token = stack_pop(stack); 31 | if (token == NULL) { 32 | *error = EXPRESSION_BAD_SYNTAX; 33 | return 0; 34 | } 35 | return token->number; 36 | } 37 | 38 | uint64_t operator_unary_minus(stack_type *stack, int *error) { 39 | expression_token_t *token = stack_pop(stack); 40 | if (token == NULL) { 41 | *error = EXPRESSION_BAD_SYNTAX; 42 | return 0; 43 | } 44 | return -token->number; 45 | } 46 | 47 | uint64_t operator_negate(stack_type *stack, int *error) { 48 | expression_token_t *token = stack_pop(stack); 49 | if (token == NULL) { 50 | *error = EXPRESSION_BAD_SYNTAX; 51 | return 0; 52 | } 53 | return ~token->number; 54 | } 55 | 56 | uint64_t operator_multiply(stack_type *stack, int *error) { 57 | expression_token_t *right = stack_pop(stack); 58 | expression_token_t *left = stack_pop(stack); 59 | if (left == NULL || right == NULL) { 60 | *error = EXPRESSION_BAD_SYNTAX; 61 | return 0; 62 | } 63 | return left->number * right->number; 64 | } 65 | 66 | uint64_t operator_divide(stack_type *stack, int *error) { 67 | expression_token_t *right = stack_pop(stack); 68 | expression_token_t *left = stack_pop(stack); 69 | if (left == NULL || right == NULL) { 70 | *error = EXPRESSION_BAD_SYNTAX; 71 | return 0; 72 | } 73 | return left->number / right->number; 74 | } 75 | 76 | uint64_t operator_logical_not(stack_type *stack, int *error) { 77 | expression_token_t *token = stack_pop(stack); 78 | if (token == NULL) { 79 | *error = EXPRESSION_BAD_SYNTAX; 80 | return 0; 81 | } 82 | return !token->number; 83 | } 84 | 85 | uint64_t operator_modulo(stack_type *stack, int *error) { 86 | expression_token_t *right = stack_pop(stack); 87 | expression_token_t *left = stack_pop(stack); 88 | if (left == NULL || right == NULL) { 89 | *error = EXPRESSION_BAD_SYNTAX; 90 | return 0; 91 | } 92 | return left->number % right->number; 93 | } 94 | 95 | uint64_t operator_left_shift(stack_type *stack, int *error) { 96 | expression_token_t *right = stack_pop(stack); 97 | expression_token_t *left = stack_pop(stack); 98 | if (left == NULL || right == NULL) { 99 | *error = EXPRESSION_BAD_SYNTAX; 100 | return 0; 101 | } 102 | return left->number << right->number; 103 | } 104 | 105 | uint64_t operator_right_shift(stack_type *stack, int *error) { 106 | expression_token_t *right = stack_pop(stack); 107 | expression_token_t *left = stack_pop(stack); 108 | if (left == NULL || right == NULL) { 109 | *error = EXPRESSION_BAD_SYNTAX; 110 | return 0; 111 | } 112 | return left->number >> right->number; 113 | } 114 | 115 | uint64_t operator_less_than(stack_type *stack, int *error) { 116 | expression_token_t *right = stack_pop(stack); 117 | expression_token_t *left = stack_pop(stack); 118 | if (left == NULL || right == NULL) { 119 | *error = EXPRESSION_BAD_SYNTAX; 120 | return 0; 121 | } 122 | return left->number < right->number; 123 | } 124 | 125 | uint64_t operator_greater_than(stack_type *stack, int *error) { 126 | expression_token_t *right = stack_pop(stack); 127 | expression_token_t *left = stack_pop(stack); 128 | if (left == NULL || right == NULL) { 129 | *error = EXPRESSION_BAD_SYNTAX; 130 | return 0; 131 | } 132 | return left->number > right->number; 133 | } 134 | 135 | uint64_t operator_less_than_or_equal_to(stack_type *stack, int *error) { 136 | expression_token_t *right = stack_pop(stack); 137 | expression_token_t *left = stack_pop(stack); 138 | if (left == NULL || right == NULL) { 139 | *error = EXPRESSION_BAD_SYNTAX; 140 | return 0; 141 | } 142 | return left->number <= right->number; 143 | } 144 | 145 | uint64_t operator_greater_than_or_equal_to(stack_type *stack, int *error) { 146 | expression_token_t *right = stack_pop(stack); 147 | expression_token_t *left = stack_pop(stack); 148 | if (left == NULL || right == NULL) { 149 | *error = EXPRESSION_BAD_SYNTAX; 150 | return 0; 151 | } 152 | return left->number >= right->number; 153 | } 154 | 155 | uint64_t operator_equal_to(stack_type *stack, int *error) { 156 | expression_token_t *right = stack_pop(stack); 157 | expression_token_t *left = stack_pop(stack); 158 | if (left == NULL || right == NULL) { 159 | *error = EXPRESSION_BAD_SYNTAX; 160 | return 0; 161 | } 162 | return left->number == right->number; 163 | } 164 | 165 | uint64_t operator_not_equal_to(stack_type *stack, int *error) { 166 | expression_token_t *right = stack_pop(stack); 167 | expression_token_t *left = stack_pop(stack); 168 | if (left == NULL || right == NULL) { 169 | *error = EXPRESSION_BAD_SYNTAX; 170 | return 0; 171 | } 172 | return left->number != right->number; 173 | } 174 | 175 | uint64_t operator_bitwise_and(stack_type *stack, int *error) { 176 | expression_token_t *right = stack_pop(stack); 177 | expression_token_t *left = stack_pop(stack); 178 | if (left == NULL || right == NULL) { 179 | *error = EXPRESSION_BAD_SYNTAX; 180 | return 0; 181 | } 182 | return left->number & right->number; 183 | } 184 | 185 | uint64_t operator_bitwise_or(stack_type *stack, int *error) { 186 | expression_token_t *right = stack_pop(stack); 187 | expression_token_t *left = stack_pop(stack); 188 | if (left == NULL || right == NULL) { 189 | *error = EXPRESSION_BAD_SYNTAX; 190 | return 0; 191 | } 192 | return left->number | right->number; 193 | } 194 | 195 | uint64_t operator_bitwise_xor(stack_type *stack, int *error) { 196 | expression_token_t *right = stack_pop(stack); 197 | expression_token_t *left = stack_pop(stack); 198 | if (left == NULL || right == NULL) { 199 | *error = EXPRESSION_BAD_SYNTAX; 200 | return 0; 201 | } 202 | return left->number ^ right->number; 203 | } 204 | 205 | uint64_t operator_logical_and(stack_type *stack, int *error) { 206 | expression_token_t *right = stack_pop(stack); 207 | expression_token_t *left = stack_pop(stack); 208 | if (left == NULL || right == NULL) { 209 | *error = EXPRESSION_BAD_SYNTAX; 210 | return 0; 211 | } 212 | return left->number && right->number; 213 | } 214 | 215 | uint64_t operator_logical_or(stack_type *stack, int *error) { 216 | expression_token_t *right = stack_pop(stack); 217 | expression_token_t *left = stack_pop(stack); 218 | if (left == NULL || right == NULL) { 219 | *error = EXPRESSION_BAD_SYNTAX; 220 | return 0; 221 | } 222 | return left->number || right->number; 223 | } 224 | -------------------------------------------------------------------------------- /common/readline.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "readline.h" 4 | 5 | char *read_line(FILE *file) { 6 | int i = 0, length = 0, size = 128; 7 | char *string = malloc(size); 8 | if (!string) { 9 | return NULL; 10 | } 11 | while (1) { 12 | int c = getc(file); 13 | if (c == EOF || c == '\n' || c == '\0') { 14 | break; 15 | } 16 | if (c == '\r') { 17 | continue; 18 | } 19 | if (i == size) { 20 | string = realloc(string, length *= 2); 21 | if (!string) { 22 | return NULL; 23 | } 24 | } 25 | string[i++] = (char)c; 26 | length++; 27 | } 28 | if (i + 1 != size) { 29 | string = realloc(string, length + 1); 30 | if (!string) { 31 | return NULL; 32 | } 33 | } 34 | string[i] = '\0'; 35 | return string; 36 | } 37 | 38 | char *read_line_s(const char *input, int *offset) { 39 | int i = 0, length = 0, size = 128; 40 | char *string = malloc(size); 41 | if (!string) { 42 | return NULL; 43 | } 44 | while (1) { 45 | int c = input[*offset]; 46 | *offset += 1; 47 | if (c == EOF || c == '\n' || c == '\0') { 48 | break; 49 | } 50 | if (c == '\r') { 51 | continue; 52 | } 53 | if (i == size) { 54 | string = realloc(string, length *= 2); 55 | if (!string) { 56 | return NULL; 57 | } 58 | } 59 | string[i++] = (char)c; 60 | length++; 61 | } 62 | if (i + 1 != size) { 63 | string = realloc(string, length + 1); 64 | if (!string) { 65 | return NULL; 66 | } 67 | } 68 | string[i] = '\0'; 69 | return string; 70 | } 71 | -------------------------------------------------------------------------------- /common/runtime.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "list.h" 5 | #include "stack.h" 6 | #include "instructions.h" 7 | #include "expression.h" 8 | #include "objects.h" 9 | #include "linker.h" 10 | #include "runtime.h" 11 | 12 | // Defines this when building for a shared library 13 | struct runtime scas_runtime; 14 | -------------------------------------------------------------------------------- /common/stack.c: -------------------------------------------------------------------------------- 1 | #include "stack.h" 2 | #include "stdlib.h" 3 | 4 | stack_type *create_stack(void) { 5 | stack_type *stack = malloc(sizeof *stack); 6 | 7 | stack->capacity = STACK_GROWTH_RATE; 8 | stack->length = 0; 9 | stack->items = calloc(STACK_GROWTH_RATE, sizeof(void*)); 10 | 11 | return stack; 12 | } 13 | 14 | void stack_free(stack_type *stack) { 15 | free(stack->items); 16 | free(stack); 17 | } 18 | 19 | void stack_push(stack_type *stack, void *item) { 20 | if (stack->capacity <= stack->length) { 21 | stack->capacity += STACK_GROWTH_RATE; 22 | stack->items = realloc(stack->items, stack->capacity); 23 | } 24 | 25 | stack->items[stack->length++] = item; 26 | } 27 | 28 | void *stack_pop(stack_type *stack) { 29 | if (stack->length == 0) { 30 | return NULL; 31 | } 32 | return stack->items[--stack->length]; 33 | } 34 | 35 | void *stack_peek(stack_type *stack) { 36 | if (stack->length == 0) { 37 | return NULL; 38 | } 39 | return stack->items[stack->length - 1]; 40 | } 41 | 42 | void stack_shrink_to_fit(stack_type *stack) { 43 | stack->capacity = stack->length; 44 | stack->items = realloc(stack->items, stack->capacity); 45 | } 46 | -------------------------------------------------------------------------------- /common/stringop.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "list.h" 6 | #include "stringop.h" 7 | 8 | /* Note: This returns 8 characters for trimmed_start per tab character. */ 9 | char *strip_whitespace(char *_str, int *trimmed_start) { 10 | *trimmed_start = 0; 11 | if (*_str == '\0') 12 | return _str; 13 | char *strold = _str; 14 | while (*_str == ' ' || *_str == '\t') { 15 | if (*_str == '\t') { 16 | *trimmed_start += 8; 17 | } else { 18 | *trimmed_start += 1; 19 | } 20 | _str++; 21 | } 22 | char *str = strdup(_str); 23 | free(strold); 24 | int i; 25 | for (i = 0; str[i] != '\0'; ++i); 26 | do { 27 | i--; 28 | } while (i >= 0 && (str[i] == ' ' || str[i] == '\t')); 29 | str[i + 1] = '\0'; 30 | return str; 31 | } 32 | 33 | char *strip_comments(char *str) { 34 | int in_string = 0, in_character = 0; 35 | int i = 0; 36 | while (str[i] != '\0') { 37 | if (str[i] == '"' && !in_character) { 38 | in_string = !in_string; 39 | } else if (str[i] == '\'' && !in_string) { 40 | in_character = !in_character; 41 | } else if (!in_character && !in_string) { 42 | if (str[i] == ';' || (str[i] == '#' && !(str[i+1] >= 'a' && str[i+1] <= 'z'))) { 43 | str[i] = '\0'; 44 | break; 45 | } 46 | } 47 | ++i; 48 | } 49 | return str; 50 | } 51 | 52 | list_t *split_string(const char *str, const char *delims) { 53 | list_t *res = create_list(); 54 | for (size_t i = 0, j = 0; i < strlen(str) + 1; ++i) { 55 | if (strchr(delims, str[i]) || i == strlen(str)) { 56 | if (i - j == 0) { 57 | continue; 58 | } 59 | char *left = malloc(i - j + 1); 60 | memcpy(left, str + j, i - j); 61 | left[i - j] = 0; 62 | list_add(res, left); 63 | j = i + 1; 64 | while (j <= strlen(str) && str[j] && strchr(delims, str[j])) { 65 | j++; 66 | i++; 67 | } 68 | } 69 | } 70 | return res; 71 | } 72 | 73 | void free_flat_list(list_t *list) { 74 | for (unsigned int i = 0; i < list->length; ++i) { 75 | free(list->items[i]); 76 | } 77 | list_free(list); 78 | } 79 | 80 | char *code_strstr(const char *haystack, const char *needle) { 81 | /* TODO */ 82 | return strstr(haystack, needle); 83 | } 84 | 85 | char *code_strchr(const char *str, char delimiter) { 86 | int in_string = 0, in_character = 0, in_paren = 0; 87 | int i = 0; 88 | while (str[i] != '\0') { 89 | if(str[i] == '\\'){ 90 | // skip the escape 91 | i++; 92 | continue; 93 | } else if (str[i] == '"' && !in_character) { 94 | in_string = !in_string; 95 | } else if (str[i] == '\'' && !in_string) { 96 | in_character = !in_character; 97 | } else if (str[i] == '(') { 98 | in_paren++; 99 | } else if (str[i] == ')' && in_paren) { 100 | in_paren--; 101 | } else if (!in_character && !in_string && !in_paren) { 102 | if (str[i] == delimiter) { 103 | return (char *)str + i; 104 | } 105 | } 106 | ++i; 107 | } 108 | return NULL; 109 | } 110 | 111 | int unescape_string(char *string) { 112 | /* TODO: More C string escapes */ 113 | int len = strlen(string); 114 | int i; 115 | for (i = 0; string[i]; ++i) { 116 | if (string[i] == '\\') { 117 | --len; 118 | switch (string[++i]) { 119 | case '0': 120 | string[i - 1] = '\0'; 121 | memmove(string + i, string + i + 1, len - i); 122 | break; 123 | case 'a': 124 | string[i - 1] = '\a'; 125 | memmove(string + i, string + i + 1, len - i); 126 | break; 127 | case 'b': 128 | string[i - 1] = '\b'; 129 | memmove(string + i, string + i + 1, len - i); 130 | break; 131 | case 't': 132 | string[i - 1] = '\t'; 133 | memmove(string + i, string + i + 1, len - i); 134 | break; 135 | case 'n': 136 | string[i - 1] = '\n'; 137 | memmove(string + i, string + i + 1, len - i); 138 | break; 139 | case 'v': 140 | string[i - 1] = '\v'; 141 | memmove(string + i, string + i + 1, len - i); 142 | break; 143 | case 'f': 144 | string[i - 1] = '\f'; 145 | memmove(string + i, string + i + 1, len - i); 146 | break; 147 | case 'r': 148 | string[i - 1] = '\r'; 149 | memmove(string + i, string + i + 1, len - i); 150 | break; 151 | case '\\': 152 | case '"': 153 | case '\'': 154 | memmove(string + i - 1, string + i, len - i + 1); 155 | break; 156 | } 157 | i--; 158 | } 159 | } 160 | return len; 161 | } 162 | -------------------------------------------------------------------------------- /doc/scas.1.scdoc: -------------------------------------------------------------------------------- 1 | SCAS(1) 2 | 3 | # NAME 4 | 5 | scas - SirCmpwn's Assembler 6 | 7 | # SYNOPSIS 8 | 9 | *scas* [options] _FILE(s)_ 10 | 11 | # DESCRIPTION 12 | 13 | scas assembles and optionally links any number of assembly and object files into a 14 | single output file. Specify "-" as an input file name to read from stdin. 15 | 16 | # OPTIONS 17 | 18 | *-a, --architecture* 19 | Specifies an architecture or instruction set file. The default is z80. If you 20 | specify a file name, that file will be used as an instruction set. Otherwise, 21 | it will be matched against scas's internal list of instruction sets. See 22 | _Architectures_ below for additional information. 23 | 24 | *-c, --compile* 25 | Compile, but do not link, the assembly sources into an object file. 26 | 27 | *-D , -D, \--define* 28 | Defines _MACRO_ as 1. You may also use *-D MACRO=STRING* to set a macro to a 29 | specific value, as if writing *#define MACRO STRING* into your code. 30 | 31 | *-f