xref: /haiku/src/tools/gensyscalls/gensyscallinfos.cpp (revision 21258e2674226d6aa732321b6f8494841895af5f)
1 /*
2  * Copyright 2004-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <cstdio>
8 #include <cstdlib>
9 #include <fstream>
10 #include <list>
11 #include <stack>
12 #include <string>
13 #include <string.h>
14 #include <vector>
15 
16 #include "gensyscalls_common.h"
17 
18 #include "arch_gensyscalls.h"
19 	// for the alignment type macros (only for the type names)
20 
21 
22 // macro trickery to create a string literal
23 #define MAKE_STRING(x)	#x
24 #define EVAL_MACRO(macro, x) macro(x)
25 
26 
27 const char* kUsage =
28 	"Usage: gensyscallinfos <header> <syscall infos> <syscall types sizes>\n"
29 	"\n"
30 	"Given the (preprocessed) header file that defines the syscall prototypes "
31 		"the\n"
32 	"command generates a source file consisting of syscall infos, which is "
33 		"needed\n"
34 	"to build gensyscalls, which in turn generates the assembly syscall\n"
35 	"definitions and code for the kernelland syscall dispatcher.\n"
36 	"\n"
37 	"  <header>               - Input: The preprocessed header file with the\n"
38 	"                           syscall prototypes.\n"
39 	"  <syscall infos>        - Output: The syscall infos source file needed "
40 		"to\n"
41 	"                           build gensyscalls.\n"
42 	"  <syscall types sizes>  - Output: A source file that will by another "
43 		"build\n"
44 	"                           step turned into a header file included by\n"
45 	"                           <syscall infos>.\n";
46 
47 
48 static void
49 print_usage(bool error)
50 {
51 	fprintf((error ? stderr : stdout), kUsage);
52 }
53 
54 
55 struct Type {
56 	Type(const char* type) : type(type) {}
57 	Type(const string& type) : type(type) {}
58 
59 	string type;
60 };
61 
62 
63 struct NamedType : Type {
64 	NamedType(const char* type, const char* name)
65 		: Type(type), name(name) {}
66 	NamedType(const string& type, const string& name)
67 		: Type(type), name(name) {}
68 
69 	string name;
70 };
71 
72 
73 class Function {
74 public:
75 	Function() : fReturnType("") {}
76 
77 	void SetName(const string& name)
78 	{
79 		fName = name;
80 	}
81 
82 	const string& GetName() const
83 	{
84 		return fName;
85 	}
86 
87 	void AddParameter(const NamedType& type)
88 	{
89 		fParameters.push_back(type);
90 	}
91 
92 	int CountParameters() const
93 	{
94 		return fParameters.size();
95 	}
96 
97 	const NamedType& ParameterAt(int index) const
98 	{
99 		return fParameters[index];
100 	}
101 
102 	void SetReturnType(const Type& type)
103 	{
104 		fReturnType = type;
105 	}
106 
107 	const Type& GetReturnType() const
108 	{
109 		return fReturnType;
110 	}
111 
112 protected:
113 	string				fName;
114 	vector<NamedType>	fParameters;
115 	Type				fReturnType;
116 };
117 
118 
119 class Syscall : public Function {
120 public:
121 	string GetKernelName() const
122 	{
123 		int baseIndex = 0;
124 		if (fName.find("_kern_") == 0)
125 			baseIndex = strlen("_kern_");
126 		else if (fName.find("sys_") == 0)
127 			baseIndex = strlen("sys_");
128 		string kernelName("_user_");
129 		kernelName.append(string(fName, baseIndex));
130 		return kernelName;
131 	}
132 };
133 
134 
135 class Tokenizer {
136 public:
137 	Tokenizer(istream& input)
138 		: fInput(input),
139 		  fHasCurrent(false)
140 	{
141 	}
142 
143 	string GetCurrentToken()
144 	{
145 		if (!fHasCurrent)
146 			throw Exception("GetCurrentToken(): No current token!");
147 		return fTokens.front();
148 	}
149 
150 	string GetNextToken()
151 	{
152 		return GetNextToken(NULL);
153 	}
154 
155 	string GetNextToken(stack<string>& skippedTokens)
156 	{
157 		return GetNextToken(&skippedTokens);
158 	}
159 
160 	string GetNextToken(stack<string>* skippedTokens)
161 	{
162 		if (fHasCurrent) {
163 			fTokens.pop_front();
164 			fHasCurrent = false;
165 		}
166 		while (fTokens.empty())
167 			_ReadLine();
168 		fHasCurrent = true;
169 		if (skippedTokens)
170 			skippedTokens->push(fTokens.front());
171 		return fTokens.front();
172 	}
173 
174 	void ExpectToken(const string& expectedToken)
175 	{
176 		string token = GetCurrentToken();
177 		if (expectedToken != token) {
178 			throw ParseException(string("Unexpected token `") + token
179 				+ "'. Expected was `" + expectedToken + "'.");
180 		}
181 	}
182 
183 	void ExpectNextToken(const string& expectedToken)
184 	{
185 		GetNextToken();
186 		ExpectToken(expectedToken);
187 	}
188 
189 	bool CheckToken(const string& expectedToken)
190 	{
191 		string token = GetCurrentToken();
192 		return (expectedToken == token);
193 	}
194 
195 	bool CheckNextToken(const string& expectedToken)
196 	{
197 		GetNextToken();
198 		bool result = CheckToken(expectedToken);
199 		if (!result)
200 			PutCurrentToken();
201 		return result;
202 	}
203 
204 	void PutCurrentToken()
205 	{
206 		if (!fHasCurrent)
207 			throw Exception("GetCurrentToken(): No current token!");
208 		fHasCurrent = false;
209 	}
210 
211 	void PutToken(string token)
212 	{
213 		if (fHasCurrent) {
214 			fTokens.pop_front();
215 			fHasCurrent = false;
216 		}
217 		fTokens.push_front(token);
218 	}
219 
220 	void PutTokens(stack<string>& tokens)
221 	{
222 		if (fHasCurrent) {
223 			fTokens.pop_front();
224 			fHasCurrent = false;
225 		}
226 		while (!tokens.empty()) {
227 			fTokens.push_front(tokens.top());
228 			tokens.pop();
229 		}
230 	}
231 
232 private:
233 	void _ReadLine()
234 	{
235 		// read the line
236 		char buffer[10240];
237 		if (!fInput.getline(buffer, sizeof(buffer)))
238 			throw EOFException("Unexpected end of input.");
239 		// parse it
240 		vector<string> line;
241 		int len = strlen(buffer);
242 		int tokenStart = 0;
243 		for (int i = 0; i < len; i++) {
244 			char c = buffer[i];
245 			if (isspace(c)) {
246 				if (tokenStart < i)
247 					line.push_back(string(buffer + tokenStart, buffer + i));
248 				tokenStart = i + 1;
249 				continue;
250 			}
251 			switch (buffer[i]) {
252 				case '#':
253 				case '(':
254 				case ')':
255 				case '*':
256 				case '&':
257 				case '[':
258 				case ']':
259 				case ';':
260 				case ',':
261 					if (tokenStart < i) {
262 						line.push_back(string(buffer + tokenStart,
263 												 buffer + i));
264 					}
265 					line.push_back(string(buffer + i, buffer + i + 1));
266 					tokenStart = i + 1;
267 					break;
268 			}
269 		}
270 		if (tokenStart < len)
271 			line.push_back(string(buffer + tokenStart, buffer + len));
272 		// drop the line, if it starts with "# <number>", as those are
273 		// directions from the pre-processor to the compiler
274 		if (line.size() >= 2) {
275 			if (line[0] == "#" && isdigit(line[1][0]))
276 				return;
277 		}
278 		for (int i = 0; i < (int)line.size(); i++)
279 			fTokens.push_back(line[i]);
280 	}
281 
282 private:
283 	istream&		fInput;
284 	list<string>	fTokens;
285 	bool			fHasCurrent;
286 };
287 
288 
289 class Main {
290 public:
291 
292 	int Run(int argc, char** argv)
293 	{
294 		// parse parameters
295 		if (argc >= 2
296 			&& (string(argv[1]) == "-h" || string(argv[1]) == "--help")) {
297 			print_usage(false);
298 			return 0;
299 		}
300 		if (argc != 4) {
301 			print_usage(true);
302 			return 1;
303 		}
304 		_ParseSyscalls(argv[1]);
305 		_WriteSyscallInfoFile(argv[2]);
306 		_WriteSyscallTypeSizes(argv[3]);
307 		return 0;
308 	}
309 
310 private:
311 	void _ParseSyscalls(const char* filename)
312 	{
313 		// open the input file
314 		ifstream file(filename, ifstream::in);
315 		if (!file.is_open())
316 			throw IOException(string("Failed to open `") + filename + "'.");
317 		// parse the syscalls
318 		Tokenizer tokenizer(file);
319 		// find "#pragma syscalls begin"
320 		while (true) {
321 			while (tokenizer.GetNextToken() != "#");
322 			stack<string> skippedTokens;
323 			if (tokenizer.GetNextToken(skippedTokens) == "pragma"
324 				&& tokenizer.GetNextToken(skippedTokens) == "syscalls"
325 				&& tokenizer.GetNextToken(skippedTokens) == "begin") {
326 				break;
327 			}
328 			tokenizer.PutTokens(skippedTokens);
329 		}
330 		// parse the functions
331 		while (!tokenizer.CheckNextToken("#")) {
332 			Syscall syscall;
333 			_ParseSyscall(tokenizer, syscall);
334 			fSyscalls.push_back(syscall);
335 		}
336 		// expect "pragma syscalls end"
337 		tokenizer.ExpectNextToken("pragma");
338 		tokenizer.ExpectNextToken("syscalls");
339 		tokenizer.ExpectNextToken("end");
340 	}
341 
342 	void _WriteSyscallInfoFile(const char* filename)
343 	{
344 		// open the syscall info file
345 		ofstream file(filename, ofstream::out | ofstream::trunc);
346 		if (!file.is_open())
347 			throw IOException(string("Failed to open `") + filename + "'.");
348 
349 		// write preamble
350 		file << "#include \"gensyscalls.h\"" << endl;
351 		file << "#include \"syscall_types_sizes.h\"" << endl;
352 		file << endl;
353 
354 		file << "const char* const kReturnTypeAlignmentType = \""
355 			EVAL_MACRO(MAKE_STRING, SYSCALL_RETURN_TYPE_ALIGNMENT_TYPE)
356 			<< "\";" << endl;
357 		file << "const char* const kParameterAlignmentType = \""
358 			EVAL_MACRO(MAKE_STRING, SYSCALL_PARAMETER_ALIGNMENT_TYPE)
359 			<< "\";" << endl;
360 		file << "const int kReturnTypeAlignmentSize = "
361 			"SYSCALL_RETURN_TYPE_ALIGNMENT_SIZE;" << endl;
362 		file << "const int kParameterAlignmentSize = "
363 			"SYSCALL_PARAMETER_ALIGNMENT_SIZE;" << endl;
364 		file << endl;
365 
366 		file << "SyscallVector* create_syscall_vector() {" << endl;
367 		file << "\tSyscallVector* syscallVector = SyscallVector::Create();"
368 			<< endl;
369 		file << "\tSyscall* syscall;" << endl;
370 
371 		// syscalls
372 		for (int i = 0; i < (int)fSyscalls.size(); i++) {
373 			const Syscall& syscall = fSyscalls[i];
374 
375 			// syscall = syscallVector->CreateSyscall("syscallName",
376 			//	"syscallKernelName");
377 			file << "\tsyscall = syscallVector->CreateSyscall(\""
378 				<< syscall.GetName() << "\", \""
379 				<< syscall.GetKernelName() << "\");" << endl;
380 
381 			const Type& returnType = syscall.GetReturnType();
382 
383 			// syscall->SetReturnType<(SYSCALL_RETURN_TYPE_SIZE_<i>,
384 			//		"returnType");
385 			file << "\tsyscall->SetReturnType("
386 				<< "SYSCALL_RETURN_TYPE_SIZE_" << i << ", \""
387 				<< returnType.type << "\");" << endl;
388 
389 			// parameters
390 			int paramCount = syscall.CountParameters();
391 			for (int k = 0; k < paramCount; k++) {
392 				const NamedType& param = syscall.ParameterAt(k);
393 				// syscall->AddParameter(SYSCALL_PARAMETER_SIZE_<i>_<k>,
394 				//		"parameterTypeName", "parameterName");
395 				file << "\tsyscall->AddParameter("
396 					<< "SYSCALL_PARAMETER_SIZE_" << i << "_" << k
397 					<< ", \"" << param.type << "\", \"" << param.name << "\");"
398 					<< endl;
399 			}
400 			file << endl;
401 		}
402 
403 		// postamble
404 		file << "\treturn syscallVector;" << endl;
405 		file << "}" << endl;
406 	}
407 
408 	void _WriteSyscallTypeSizes(const char* filename)
409 	{
410 		// open the syscall info file
411 		ofstream file(filename, ofstream::out | ofstream::trunc);
412 		if (!file.is_open())
413 			throw IOException(string("Failed to open `") + filename + "'.");
414 
415 		// write preamble
416 		file << "#include <computed_asm_macros.h>" << endl;
417 		file << "#include <syscalls.h>" << endl;
418 		file << endl;
419 		file << "#include \"arch_gensyscalls.h\"" << endl;
420 		file << endl;
421 		file << "void dummy() {" << endl;
422 
423 		file << "DEFINE_COMPUTED_ASM_MACRO(SYSCALL_RETURN_TYPE_ALIGNMENT_SIZE, "
424 			"sizeof(SYSCALL_RETURN_TYPE_ALIGNMENT_TYPE));" << endl;
425   		file << "DEFINE_COMPUTED_ASM_MACRO(SYSCALL_PARAMETER_ALIGNMENT_SIZE, "
426 			"sizeof(SYSCALL_PARAMETER_ALIGNMENT_TYPE));" << endl;
427 		file << endl;
428 
429 		// syscalls
430 		for (int i = 0; i < (int)fSyscalls.size(); i++) {
431 			const Syscall& syscall = fSyscalls[i];
432 			const Type& returnType = syscall.GetReturnType();
433 
434 			if (returnType.type == "void") {
435 				file << "DEFINE_COMPUTED_ASM_MACRO(SYSCALL_RETURN_TYPE_SIZE_"
436 					<< i << ", 0);" << endl;
437 			} else {
438 				file << "DEFINE_COMPUTED_ASM_MACRO(SYSCALL_RETURN_TYPE_SIZE_"
439 					<< i << ", sizeof(" << returnType.type << "));" << endl;
440 			}
441 
442 			// parameters
443 			int paramCount = syscall.CountParameters();
444 			for (int k = 0; k < paramCount; k++) {
445 				const NamedType& param = syscall.ParameterAt(k);
446 				file << "DEFINE_COMPUTED_ASM_MACRO(SYSCALL_PARAMETER_SIZE_" << i
447 					<< "_" << k << ", sizeof(" << param.type << "));" << endl;
448 			}
449 			file << endl;
450 		}
451 
452 		// postamble
453 		file << "}" << endl;
454 	}
455 
456 	void _ParseSyscall(Tokenizer& tokenizer, Syscall& syscall)
457 	{
458 		// get return type and function name
459 		vector<string> returnType;
460 		while (tokenizer.GetNextToken() != "(") {
461 			string token = tokenizer.GetCurrentToken();
462 			// strip leading "extern"
463 			if (!returnType.empty() || token != "extern")
464 				returnType.push_back(token);
465 		}
466 		if (returnType.size() < 2) {
467 			throw ParseException("Error while parsing function "
468 				"return type.");
469 		}
470 		syscall.SetName(returnType[returnType.size() - 1]);
471 		returnType.pop_back();
472 		string returnTypeString(returnType[0]);
473 		for (int i = 1; i < (int)returnType.size(); i++) {
474 			returnTypeString += " ";
475 			returnTypeString += returnType[i];
476 		}
477 		syscall.SetReturnType(returnTypeString);
478 		// get arguments
479 		if (!tokenizer.CheckNextToken(")")) {
480 			_ParseParameter(tokenizer, syscall);
481 			while (!tokenizer.CheckNextToken(")")) {
482 				tokenizer.ExpectNextToken(",");
483 				_ParseParameter(tokenizer, syscall);
484 			}
485 		}
486 		tokenizer.ExpectNextToken(";");
487 	}
488 
489 	void _ParseParameter(Tokenizer& tokenizer, Syscall& syscall)
490 	{
491 		vector<string> type;
492 		while (tokenizer.GetNextToken() != ")"
493 			&& tokenizer.GetCurrentToken() != ",") {
494 			string token = tokenizer.GetCurrentToken();
495 			type.push_back(token);
496 			if (token == "(") {
497 				// This must be a function pointer. We deal with that in a
498 				// separate method.
499 				_ParseFunctionPointerParameter(tokenizer, syscall, type);
500 				return;
501 			}
502 		}
503 		tokenizer.PutCurrentToken();
504 		if (type.size() < 2) {
505 			if (type.size() == 1 && type[0] == "void") {
506 				// that's OK
507 				return;
508 			}
509 			throw ParseException("Error while parsing function parameter.");
510 		}
511 
512 		// last component is the parameter name
513 		string typeName = type.back();
514 		type.pop_back();
515 
516 		string typeString(type[0]);
517 		for (int i = 1; i < (int)type.size(); i++) {
518 			typeString += " ";
519 			typeString += type[i];
520 		}
521 		syscall.AddParameter(NamedType(typeString, typeName));
522 	}
523 
524 	void _ParseFunctionPointerParameter(Tokenizer& tokenizer, Syscall& syscall,
525 		vector<string>& type)
526 	{
527 		// When this method is entered, the return type and the opening
528 		// parenthesis must already be parse and stored in the supplied type
529 		// vector.
530 		if (type.size() < 2) {
531 			throw ParseException("Error parsing function parameter. "
532 				"No return type.");
533 		}
534 		// read all the "*"s there are
535 		while (tokenizer.CheckNextToken("*"))
536 			type.push_back("*");
537 		// now comes the parameter name, if specified -- skip it
538 		string typeName;
539 		if (!tokenizer.CheckNextToken(")")) {
540 			typeName = tokenizer.GetNextToken();
541 			tokenizer.ExpectNextToken(")");
542 		} else {
543 			throw ParseException("Error while parsing function parameter. "
544 				"No parameter name.");
545 		}
546 		type.push_back(")");
547 		// the function parameters
548 		tokenizer.ExpectNextToken("(");
549 		type.push_back("(");
550 		while (!tokenizer.CheckNextToken(")"))
551 			type.push_back(tokenizer.GetNextToken());
552 		type.push_back(")");
553 		// compose the type string and add it to the syscall parameters
554 		string typeString(type[0]);
555 		for (int i = 1; i < (int)type.size(); i++) {
556 			typeString += " ";
557 			typeString += type[i];
558 		}
559 		syscall.AddParameter(NamedType(typeString, typeName));
560 	}
561 
562 private:
563 	vector<Syscall>	fSyscalls;
564 };
565 
566 
567 int
568 main(int argc, char** argv)
569 {
570 	try {
571 		return Main().Run(argc, argv);
572 	} catch (const Exception& exception) {
573 		fprintf(stderr, "%s\n", exception.what());
574 		return 1;
575 	}
576 }
577