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