1 /* radare - LGPL - Copyright 2011-2018 - pancake */
2
3 #include <r_egg.h>
4
5 // compilation environment
6 struct cEnv_t {
7 char *SFLIBPATH;
8 char *CC;
9 const char *OBJCOPY;
10 char *CFLAGS;
11 char *LDFLAGS;
12 const char *JMP;
13 const char *FMT;
14 char *SHDR;
15 char *TRIPLET;
16 const char *TEXT;
17 };
18
r_egg_Cfile_getCompiler(void)19 static char* r_egg_Cfile_getCompiler(void) {
20 size_t i;
21 const char *compilers[] = {"llvm-gcc", "clang", "gcc"};
22 char *output = r_sys_getenv ("CC");
23
24 if (output) {
25 return output;
26 }
27
28 for (i = 0; i < 3; i++) {
29 output = r_file_path (compilers[i]);
30 if (strcmp (output, compilers[i])) {
31 free (output);
32 return strdup (compilers[i]);
33 }
34 free (output);
35 }
36
37 eprintf ("Couldn't find a compiler ! Please, set CC.\n");
38 return NULL;
39 }
40
r_egg_Cfile_armOrMips(const char * arch)41 static inline bool r_egg_Cfile_armOrMips(const char *arch) {
42 return (!strcmp (arch, "arm") || !strcmp (arch, "arm64") || !strcmp (arch, "aarch64")
43 || !strcmp (arch, "thumb") || !strcmp (arch, "arm32") || !strcmp (arch, "mips")
44 || !strcmp (arch, "mips32") || !strcmp (arch, "mips64"));
45 }
46
r_egg_Cfile_free_cEnv(struct cEnv_t * cEnv)47 static void r_egg_Cfile_free_cEnv(struct cEnv_t *cEnv) {
48 if (cEnv) {
49 free (cEnv->SFLIBPATH);
50 free (cEnv->CC);
51 free (cEnv->CFLAGS);
52 free (cEnv->LDFLAGS);
53 free (cEnv->SHDR);
54 free (cEnv->TRIPLET);
55 }
56 free (cEnv);
57 }
58
r_egg_Cfile_check_cEnv(struct cEnv_t * cEnv)59 static inline bool r_egg_Cfile_check_cEnv(struct cEnv_t *cEnv) {
60 return (!cEnv->SFLIBPATH || !cEnv->CC || !cEnv->CFLAGS || !cEnv->LDFLAGS
61 || !cEnv->SHDR || !cEnv->TRIPLET);
62 }
63
isXNU(const char * os)64 static inline bool isXNU(const char *os) {
65 return (!strcmp (os, "darwin") || !strcmp (os, "macos")
66 || !strcmp (os, "tvos") || !strcmp (os, "watchos") || !strcmp (os, "ios"));
67 }
68
r_egg_Cfile_set_cEnv(const char * arch,const char * os,int bits)69 static struct cEnv_t* r_egg_Cfile_set_cEnv(const char *arch, const char *os, int bits) {
70 struct cEnv_t *cEnv = calloc (1, sizeof (struct cEnv_t));
71 bool use_clang;
72 char *buffer = NULL;
73 char *output = NULL;
74
75 if (!cEnv) {
76 return NULL;
77 }
78
79 if (!(cEnv->CC = r_egg_Cfile_getCompiler())) {
80 goto fail;
81 }
82
83 cEnv->SFLIBPATH = r_sys_getenv ("SFLIBPATH");
84 if (!cEnv->SFLIBPATH) {
85 output = r_sys_cmd_strf ("r2 -hh | grep INCDIR | awk '{print $2}'");
86 if (!output || (output[0] == '\0')) {
87 eprintf ("Cannot find SFLIBPATH env var.\n"
88 "Please define it, or fix r2 installation.\n");
89 goto fail;
90 }
91
92 output[strlen (output) - 1] = '\0'; // strip the ending '\n'
93 if (!(cEnv->SFLIBPATH = r_str_newf ("%s/sflib", output))) {
94 goto fail;
95 }
96 }
97
98 cEnv->JMP = r_egg_Cfile_armOrMips (arch) ? "b" : "jmp";
99
100 // TODO: Missing -Os .. caused some rip-relative LEA to be MOVQ on PIE in CLANG.. so sad
101 if (isXNU (os)) {
102 cEnv->OBJCOPY = "gobjcopy";
103 cEnv->FMT = "mach0";
104 if (!strcmp (arch, "x86")) {
105 if (bits == 32) {
106 cEnv->CFLAGS = strdup ("-arch i386 -fPIC -fPIE");
107 cEnv->LDFLAGS = strdup ("-arch i386 -shared -c -fPIC -fPIE -pie");
108 } else {
109 cEnv->CFLAGS = strdup ("-arch x86_64 -fPIC -fPIE");
110 cEnv->LDFLAGS = strdup ("-arch x86_64 -shared -c -fPIC -fPIE -pie");
111 }
112 } else {
113 cEnv->CFLAGS = strdup ("-shared -c -fPIC -pie -fPIE");
114 cEnv->LDFLAGS = strdup ("-shared -c -fPIC -pie -fPIE");
115 }
116 cEnv->SHDR = r_str_newf ("\n.text\n%s _main\n", cEnv->JMP);
117 } else {
118 cEnv->OBJCOPY = "objcopy";
119 cEnv->FMT = "elf";
120 cEnv->SHDR = r_str_newf ("\n.section .text\n.globl main\n"
121 "// .type main, @function\n%s main\n", cEnv->JMP);
122 if (!strcmp (arch, "x86")) {
123 if (bits == 32) {
124 cEnv->CFLAGS = strdup ("-fPIC -fPIE -pie -fpic -m32");
125 cEnv->LDFLAGS = strdup ("-fPIC -fPIE -pie -fpic -m32");
126 } else {
127 cEnv->CFLAGS = strdup ("-fPIC -fPIE -pie -fpic -m64");
128 cEnv->LDFLAGS = strdup ("-fPIC -fPIE -pie -fpic -m64");
129 }
130 } else {
131 cEnv->CFLAGS = strdup ("-fPIC -fPIE -pie -fpic -nostartfiles");
132 cEnv->LDFLAGS = strdup ("-fPIC -fPIE -pie -fpic -nostartfiles");
133 }
134 }
135
136 cEnv->TRIPLET = r_str_newf ("%s-%s-%d", os, arch, bits);
137
138 if (!strcmp (os, "windows")) {
139 cEnv->TEXT = ".text";
140 cEnv->FMT = "pe";
141 } else if (isXNU(os)) {
142 //cEnv->TEXT = "0.__TEXT.__text";
143 cEnv->TEXT = "0..__text";
144 } else {
145 cEnv->TEXT = ".text";
146 }
147
148 use_clang = false;
149 if (!strcmp (cEnv->TRIPLET, "darwin-arm-64")) {
150 free (cEnv->CC);
151 cEnv->CC = strdup ("xcrun --sdk iphoneos gcc -arch arm64 -miphoneos-version-min=0.0");
152 use_clang = true;
153 cEnv->TEXT = "0.__TEXT.__text";
154 } else if (!strcmp (cEnv->TRIPLET, "darwin-arm-32")) {
155 free (cEnv->CC);
156 cEnv->CC = strdup ("xcrun --sdk iphoneos gcc -arch armv7 -miphoneos-version-min=0.0");
157 use_clang = true;
158 cEnv->TEXT = "0.__TEXT.__text";
159 }
160
161 buffer = r_str_newf ("%s -fno-stack-protector -nostdinc -include '%s'/'%s'/sflib.h",
162 cEnv->CFLAGS, cEnv->SFLIBPATH, cEnv->TRIPLET);
163 if (!buffer) {
164 goto fail;
165 }
166 free (cEnv->CFLAGS);
167 cEnv->CFLAGS = strdup (buffer);
168
169 if (use_clang) {
170 free (buffer);
171 buffer = r_str_newf ("%s -fomit-frame-pointer"
172 " -fno-zero-initialized-in-bss", cEnv->CFLAGS);
173 if (!buffer) {
174 goto fail;
175 }
176 free (cEnv->CFLAGS);
177 cEnv->CFLAGS = strdup (buffer);
178 } else {
179 free (buffer);
180 buffer = r_str_newf ("%s -z execstack -fomit-frame-pointer"
181 " -finline-functions -fno-zero-initialized-in-bss", cEnv->CFLAGS);
182 if (!buffer) {
183 goto fail;
184 }
185 free (cEnv->CFLAGS);
186 cEnv->CFLAGS = strdup (buffer);
187 }
188 free (buffer);
189 buffer = r_str_newf ("%s -nostdlib", cEnv->LDFLAGS);
190 if (!buffer) {
191 goto fail;
192 }
193 free (cEnv->LDFLAGS);
194 cEnv->LDFLAGS = strdup (buffer);
195
196 if (r_egg_Cfile_check_cEnv (cEnv)) {
197 eprintf ("Error with cEnv allocation!\n");
198 goto fail;
199 }
200
201 free (buffer);
202 free (output);
203 return cEnv;
204
205 fail:
206 free (buffer);
207 free (output);
208 r_egg_Cfile_free_cEnv (cEnv);
209 return NULL;
210 }
211
r_egg_Cfile_parseCompiled(const char * file)212 static bool r_egg_Cfile_parseCompiled(const char *file) {
213 char *fileExt = r_str_newf ("%s.tmp", file);
214 char *buffer = r_file_slurp (fileExt, NULL);
215 if (!buffer) {
216 eprintf ("Could not open '%s'.\n", fileExt);
217 goto fail;
218 }
219
220 buffer = r_str_replace (buffer, "rdata", "text", false);
221 buffer = r_str_replace (buffer, "rodata", "text", false);
222 buffer = r_str_replace (buffer, "get_pc_thunk.bx", "__getesp__", true);
223
224 const char *words[] = {".cstring", "size", "___main", "section", "__alloca", "zero", "cfi"};
225 size_t i;
226 for (i = 0; i < 7; i++) {
227 r_str_stripLine (buffer, words[i]);
228 }
229
230 free (fileExt);
231 fileExt = r_str_newf ("%s.s", file);
232 if (!r_file_dump (fileExt, (const ut8*) buffer, strlen (buffer), true)) {
233 eprintf ("Error while opening %s.s\n", file);
234 goto fail;
235 }
236
237 free (buffer);
238 free (fileExt);
239 return true;
240
241 fail:
242 free (buffer);
243 free (fileExt);
244 return false;
245 }
246
r_egg_Cfile_parser(const char * file,const char * arch,const char * os,int bits)247 R_API char* r_egg_Cfile_parser(const char *file, const char *arch, const char *os, int bits) {
248 char *output = NULL;
249 char *fileExt = NULL; // "file" with extension (.s, .text, ...)
250 struct cEnv_t *cEnv = r_egg_Cfile_set_cEnv (arch, os, bits);
251
252 if (!cEnv) {
253 goto fail;
254 }
255
256 r_str_sanitize (cEnv->CC);
257
258 // Compile
259 char *cmd = r_str_newf ("'%s' %s -o '%s.tmp' -S '%s'\n", cEnv->CC, cEnv->CFLAGS, file, file);
260 eprintf ("%s\n", cmd);
261 int rc = r_sys_cmd (cmd);
262 free (cmd);
263 if (rc != 0) {
264 goto fail;
265 }
266 if (!(fileExt = r_str_newf ("%s.s", file))) {
267 goto fail;
268 }
269
270 if (!r_file_dump (fileExt, (const ut8*) cEnv->SHDR, strlen (cEnv->SHDR), false)) {
271 eprintf ("Error while opening %s.s\n", file);
272 goto fail;
273 }
274
275 if (!r_egg_Cfile_parseCompiled (file)) {
276 goto fail;
277 }
278 // Assemble
279 cmd = r_str_newf ("'%s' %s -o '%s.o' '%s.s'", cEnv->CC, cEnv->LDFLAGS, file, file);
280 eprintf ("%s\n", cmd);
281 rc = r_sys_cmd (cmd);
282 free (cmd);
283 if (rc != 0) {
284 goto fail;
285 }
286
287 // Link
288 printf ("rabin2 -o '%s.text' -O d/S/'%s' '%s.o'\n", file, cEnv->TEXT, file);
289 output = r_sys_cmd_strf ("rabin2 -o '%s.text' -O d/S/'%s' '%s'.o",
290 file, cEnv->TEXT, file);
291 if (!output) {
292 eprintf ("Linkage failed!\n");
293 goto fail;
294 }
295
296 free (fileExt);
297 if (!(fileExt = r_str_newf ("%s.o", file))) {
298 goto fail;
299 }
300
301 if (!r_file_exists (fileExt)) {
302 eprintf ("Cannot find %s.o\n", file);
303 goto fail;
304 }
305
306 free (fileExt);
307 if (!(fileExt = r_str_newf ("%s.text", file))) {
308 goto fail;
309 }
310 if (r_file_size (fileExt) == 0) {
311 eprintf ("FALLBACK: Using objcopy instead of rabin2");
312 free (output);
313 output = r_sys_cmd_strf ("'%s' -j .text -O binary '%s.o' '%s.text'",
314 cEnv->OBJCOPY, file, file);
315 if (!output) {
316 eprintf ("objcopy failed!\n");
317 goto fail;
318 }
319 }
320
321 size_t i;
322 const char *extArray[] = {"bin", "tmp", "s", "o"};
323 for (i = 0; i < 4; i++) {
324 free (fileExt);
325 if (!(fileExt = r_str_newf ("%s.%s", file, extArray[i]))) {
326 goto fail;
327 }
328 r_file_rm (fileExt);
329 }
330
331 free (fileExt);
332 if ((fileExt = r_str_newf ("%s.text", file)) == NULL) {
333 goto fail;
334 }
335
336 free (output);
337 r_egg_Cfile_free_cEnv (cEnv);
338 return fileExt;
339
340 fail:
341 free (fileExt);
342 free (output);
343 r_egg_Cfile_free_cEnv (cEnv);
344 return NULL;
345 }
346