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