1/*
2 * Android "Almost" C Compiler.
3 * This is a compiler for a small subset of the C language, intended for use
4 * in scripting environments where speed and memory footprint are important.
5 *
6 * This code is based upon the "unobfuscated" version of the
7 * Obfuscated Tiny C compiler, see the file LICENSE for details.
8 *
9 */
10
11#include <ctype.h>
12#include <dlfcn.h>
13#include <stdarg.h>
14#include <stdint.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18
19#if defined(__arm__)
20#include <unistd.h>
21#endif
22
23#if defined(__arm__)
24#define PROVIDE_ARM_DISASSEMBLY
25#endif
26
27#ifdef PROVIDE_ARM_DISASSEMBLY
28#include "disassem.h"
29#endif
30
31#include <acc/acc.h>
32
33
34typedef int (*MainPtr)(int, char**);
35// This is a separate function so it can easily be set by breakpoint in gdb.
36int run(MainPtr mainFunc, int argc, char** argv) {
37    return mainFunc(argc, argv);
38}
39
40ACCvoid* symbolLookup(ACCvoid* pContext, const ACCchar* name) {
41    return (ACCvoid*) dlsym(RTLD_DEFAULT, name);
42}
43
44#ifdef PROVIDE_ARM_DISASSEMBLY
45
46static FILE* disasmOut;
47
48static u_int
49disassemble_readword(u_int address)
50{
51    return(*((u_int *)address));
52}
53
54static void
55disassemble_printaddr(u_int address)
56{
57    fprintf(disasmOut, "0x%08x", address);
58}
59
60static void
61disassemble_printf(const char *fmt, ...) {
62    va_list ap;
63    va_start(ap, fmt);
64    vfprintf(disasmOut, fmt, ap);
65    va_end(ap);
66}
67
68static int disassemble(ACCscript* script, FILE* out) {
69    disasmOut = out;
70    disasm_interface_t  di;
71    di.di_readword = disassemble_readword;
72    di.di_printaddr = disassemble_printaddr;
73    di.di_printf = disassemble_printf;
74
75    ACCvoid* base;
76    ACCsizei length;
77
78    accGetProgramBinary(script, &base, &length);
79    unsigned long* pBase = (unsigned long*) base;
80    unsigned long* pEnd = (unsigned long*) (((unsigned char*) base) + length);
81
82    for(unsigned long* pInstruction = pBase; pInstruction < pEnd; pInstruction++) {
83        fprintf(out, "%08x: %08x  ", (int) pInstruction, (int) *pInstruction);
84        ::disasm(&di, (uint) pInstruction, 0);
85    }
86    return 0;
87}
88
89#endif // PROVIDE_ARM_DISASSEMBLY
90
91int main(int argc, char** argv) {
92    const char* inFile = NULL;
93    bool printListing;
94    bool runResults = false;
95    FILE* in = stdin;
96    int i;
97    for (i = 1; i < argc; i++) {
98        char* arg = argv[i];
99        if (arg[0] == '-') {
100            switch (arg[1]) {
101                case 'S':
102                    printListing = true;
103                    break;
104                case 'R':
105                    runResults = true;
106                    break;
107            default:
108                fprintf(stderr, "Unrecognized flag %s\n", arg);
109                return 3;
110            }
111        } else if (inFile == NULL) {
112            inFile = arg;
113        } else {
114            break;
115        }
116    }
117
118    if (! inFile) {
119        fprintf(stderr, "input file required\n");
120        return 2;
121    }
122
123    if (inFile) {
124        in = fopen(inFile, "r");
125        if (!in) {
126            fprintf(stderr, "Could not open input file %s\n", inFile);
127            return 1;
128        }
129    }
130
131    fseek(in, 0, SEEK_END);
132    size_t fileSize = (size_t) ftell(in);
133    rewind(in);
134    ACCchar* text = new ACCchar[fileSize + 1];
135    size_t bytesRead = fread(text, 1, fileSize, in);
136    if (bytesRead != fileSize) {
137        fprintf(stderr, "Could not read all of file %s\n", inFile);
138    }
139
140    text[fileSize] = '\0';
141
142    ACCscript* script = accCreateScript();
143
144    const ACCchar* scriptSource[] = {text};
145    accScriptSource(script, 1, scriptSource, NULL);
146    delete[] text;
147
148    accRegisterSymbolCallback(script, symbolLookup, NULL);
149
150    accCompileScript(script);
151    int result = accGetError(script);
152    MainPtr mainPointer = 0;
153    if (result != 0) {
154        ACCsizei bufferLength;
155        accGetScriptInfoLog(script, 0, &bufferLength, NULL);
156        char* buf = (char*) malloc(bufferLength + 1);
157        if (buf != NULL) {
158            accGetScriptInfoLog(script, bufferLength + 1, NULL, buf);
159            fprintf(stderr, "%s", buf);
160            free(buf);
161        } else {
162            fprintf(stderr, "Out of memory.\n");
163        }
164        goto exit;
165    }
166
167    {
168        ACCsizei numPragmaStrings;
169        accGetPragmas(script, &numPragmaStrings, 0, NULL);
170        if (numPragmaStrings) {
171            char** strings = new char*[numPragmaStrings];
172            accGetPragmas(script, NULL, numPragmaStrings, strings);
173            for(ACCsizei i = 0; i < numPragmaStrings; i += 2) {
174                fprintf(stderr, "#pragma %s(%s)\n", strings[i], strings[i+1]);
175            }
176            delete[] strings;
177        }
178    }
179
180    if (printListing) {
181#ifdef PROVIDE_ARM_DISASSEMBLY
182        disassemble(script, stderr);
183#endif
184    }
185
186    if (runResults) {
187        accGetScriptLabel(script, "main", (ACCvoid**) & mainPointer);
188
189        result = accGetError(script);
190        if (result != ACC_NO_ERROR) {
191            fprintf(stderr, "Could not find main: %d\n", result);
192        } else {
193            fprintf(stderr, "Executing compiled code:\n");
194            int codeArgc = argc - i + 1;
195            char** codeArgv = argv + i - 1;
196            codeArgv[0] = (char*) (inFile ? inFile : "stdin");
197            result = run(mainPointer, codeArgc, codeArgv);
198            fprintf(stderr, "result: %d\n", result);
199        }
200    }
201
202exit:
203
204    accDeleteScript(script);
205
206    return result;
207}
208