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