xref: /haiku/src/tools/gensyscalls/gensyscalls.cpp (revision f75a7bf508f3156d63a14f8fd77c5e0ca4d08c42)
1 /*
2  * Copyright 2004-2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 // Don't include arch_gensyscalls.h. It's only needed when creating the
7 // syscall vector.
8 #define DONT_INCLUDE_ARCH_GENSYSCALLS_H 1
9 
10 #include <cstdio>
11 #include <cstdlib>
12 #include <fstream>
13 #include <string>
14 #include <string.h>
15 #include <vector>
16 
17 #include "gensyscalls.h"
18 #include "gensyscalls_common.h"
19 
20 using std::vector;
21 
22 // usage
23 const char *kUsage =
24 "Usage: gensyscalls [ -c <calls> ] [ -d <dispatcher> ] [ -n <numbers> ]\n"
25 "                   [ -t <table> ] [ -s <strace> ]\n"
26 "\n"
27 "The command is able to generate several syscalls related source files.\n"
28 "\n"
29 "  <calls>                - Output: The assembly source file implementing the\n"
30 "                           actual syscalls.\n"
31 "  <dispatcher>           - Output: The C source file to be included by the\n"
32 "                           syscall dispatcher source file.\n"
33 "  <numbers>              - Output: The C/assembly include files defining the\n"
34 "                           syscall numbers.\n"
35 "  <table>                - Output: A C source file containing an array with\n"
36 "                           infos about the syscalls\n"
37 "  <strace>               - Output: A C source file for strace support.\n"
38 ;
39 
40 // print_usage
41 static
42 void
43 print_usage(bool error)
44 {
45 	fprintf((error ? stderr : stdout), kUsage);
46 }
47 
48 
49 // Type
50 
51 // constructor
52 Type::Type(const char* name, int size, int usedSize,
53 	const char* alignmentTypeName)
54 	: fName(name),
55 	  fSize(size),
56 	  fUsedSize(usedSize),
57 	  fAlignmentType(alignmentTypeName)
58 {
59 }
60 
61 
62 // Parameter
63 
64 // constructor
65 Parameter::Parameter(const char* typeName, const char* parameterName,
66 	int size, int usedSize, int offset, const char* alignmentTypeName)
67 	: Type(typeName, size, usedSize, alignmentTypeName),
68 	  fParameterName(parameterName),
69 	  fOffset(offset)
70 {
71 }
72 
73 
74 // Syscall
75 
76 // ParameterVector
77 struct Syscall::ParameterVector : public vector<Parameter*> {
78 };
79 
80 // constructor
81 Syscall::Syscall(const char* name, const char* kernelName)
82 	: fName(name),
83 	  fKernelName(kernelName),
84 	  fReturnType(NULL),
85 	  fParameters(new ParameterVector)
86 {
87 }
88 
89 // destructor
90 Syscall::~Syscall()
91 {
92 	delete fReturnType;
93 
94 	int count = CountParameters();
95 	for (int i = 0; i < count; i++)
96 		delete ParameterAt(i);
97 	delete fParameters;
98 }
99 
100 // ParameterAt
101 int
102 Syscall::CountParameters() const
103 {
104 	return fParameters->size();
105 }
106 
107 // ParameterAt
108 Parameter*
109 Syscall::ParameterAt(int index) const
110 {
111 	if (index < 0 || index >= CountParameters())
112 		return NULL;
113 
114 	return (*fParameters)[index];
115 }
116 
117 // LastParameter
118 Parameter*
119 Syscall::LastParameter() const
120 {
121 	return ParameterAt(CountParameters() - 1);
122 }
123 
124 // SetReturnType
125 Type*
126 Syscall::SetReturnType(const char* name, int size, int usedSize,
127 	const char* alignmentTypeName)
128 {
129 	delete fReturnType;
130 	return fReturnType = new Type(name, size, usedSize, alignmentTypeName);
131 }
132 
133 // AddParameter
134 Parameter*
135 Syscall::AddParameter(const char* typeName, const char* parameterName,
136 	int size, int usedSize, int offset, const char* alignmentTypeName)
137 {
138 	Parameter* parameter = new Parameter(typeName, parameterName, size,
139 		usedSize, offset, alignmentTypeName);
140 	fParameters->push_back(parameter);
141 	return parameter;
142 }
143 
144 
145 // SyscallVector
146 
147 struct SyscallVector::_SyscallVector : public vector<Syscall*> {
148 };
149 
150 
151 // constructor
152 SyscallVector::SyscallVector()
153 	: fSyscalls(new _SyscallVector)
154 {
155 }
156 
157 // destructor
158 SyscallVector::~SyscallVector()
159 {
160 	int count = CountSyscalls();
161 	for (int i = 0; i < count; i++)
162 		delete SyscallAt(i);
163 	delete fSyscalls;
164 }
165 
166 // Create
167 SyscallVector*
168 SyscallVector::Create()
169 {
170 	return new SyscallVector;
171 }
172 
173 // CountSyscalls
174 int
175 SyscallVector::CountSyscalls() const
176 {
177 	return fSyscalls->size();
178 }
179 
180 // SyscallAt
181 Syscall*
182 SyscallVector::SyscallAt(int index) const
183 {
184 	if (index < 0 || index >= CountSyscalls())
185 		return NULL;
186 
187 	return (*fSyscalls)[index];
188 }
189 
190 // CreateSyscall
191 Syscall*
192 SyscallVector::CreateSyscall(const char* name, const char* kernelName)
193 {
194 	Syscall* syscall = new Syscall(name, kernelName);
195 	fSyscalls->push_back(syscall);
196 	return syscall;
197 }
198 
199 
200 // #pragma mark -
201 
202 // Main
203 class Main {
204 public:
205 
206 	int Run(int argc, char **argv)
207 	{
208 		// parse arguments
209 		const char *syscallsFile = NULL;
210 		const char *dispatcherFile = NULL;
211 		const char *numbersFile = NULL;
212 		const char *tableFile = NULL;
213 		const char *straceFile = NULL;
214 		for (int argi = 1; argi < argc; argi++) {
215 			string arg(argv[argi]);
216 			if (arg == "-h" || arg == "--help") {
217 				print_usage(false);
218 				return 0;
219 			} else if (arg == "-c") {
220 				if (argi + 1 >= argc) {
221 					print_usage(true);
222 					return 1;
223 				}
224 				syscallsFile = argv[++argi];
225 			} else if (arg == "-d") {
226 				if (argi + 1 >= argc) {
227 					print_usage(true);
228 					return 1;
229 				}
230 				dispatcherFile = argv[++argi];
231 			} else if (arg == "-n") {
232 				if (argi + 1 >= argc) {
233 					print_usage(true);
234 					return 1;
235 				}
236 				numbersFile = argv[++argi];
237 			} else if (arg == "-t") {
238 				if (argi + 1 >= argc) {
239 					print_usage(true);
240 					return 1;
241 				}
242 				tableFile = argv[++argi];
243 			} else if (arg == "-s") {
244 				if (argi + 1 >= argc) {
245 					print_usage(true);
246 					return 1;
247 				}
248 				straceFile = argv[++argi];
249 			} else {
250 				print_usage(true);
251 				return 1;
252 			}
253 		}
254 		fSyscallVector = create_syscall_vector();
255 		fSyscallCount = fSyscallVector->CountSyscalls();
256 		if (!syscallsFile && !dispatcherFile && !numbersFile && !tableFile
257 			&& !straceFile) {
258 			printf("Found %d syscalls.\n", fSyscallCount);
259 			return 0;
260 		}
261 		// generate the output
262 		if (syscallsFile)
263 			_WriteSyscallsFile(syscallsFile);
264 		if (dispatcherFile)
265 			_WriteDispatcherFile(dispatcherFile);
266 		if (numbersFile)
267 			_WriteNumbersFile(numbersFile);
268 		if (tableFile)
269 			_WriteTableFile(tableFile);
270 		if (straceFile)
271 			_WriteSTraceFile(straceFile);
272 		return 0;
273 	}
274 
275 	void _WriteSyscallsFile(const char *filename)
276 	{
277 		// open the syscalls output file
278 		ofstream file(filename, ofstream::out | ofstream::trunc);
279 		if (!file.is_open())
280 			throw IOException(string("Failed to open `") + filename + "'.");
281 
282 		// output the syscalls definitions
283 		for (int i = 0; i < fSyscallCount; i++) {
284 			const Syscall* syscall = fSyscallVector->SyscallAt(i);
285 			int paramCount = syscall->CountParameters();
286 			int paramSize = 0;
287 			// XXX: Currently the SYSCALL macros support 4 byte aligned
288 			// parameters only. This has to change, of course.
289 			for (int k = 0; k < paramCount; k++) {
290 				const Parameter* parameter = syscall->ParameterAt(k);
291 				int size = parameter->UsedSize();
292 				paramSize += (size + 3) / 4 * 4;
293 			}
294 			file << "SYSCALL" << (paramSize / 4) << "("
295 				<< syscall->Name() << ", " << i << ")" << endl;
296 		}
297 	}
298 
299 	void _WriteDispatcherFile(const char *filename)
300 	{
301 		// open the dispatcher output file
302 		ofstream file(filename, ofstream::out | ofstream::trunc);
303 		if (!file.is_open())
304 			throw IOException(string("Failed to open `") + filename + "'.");
305 
306 		// output the case statements
307 		for (int i = 0; i < fSyscallCount; i++) {
308 			const Syscall* syscall = fSyscallVector->SyscallAt(i);
309 			file << "case " << i << ":" << endl;
310 			file << "\t";
311 			if (string(syscall->ReturnType()->TypeName()) != "void")
312 				file << "*call_ret = ";
313 			file << syscall->KernelName() << "(";
314 			int paramCount = syscall->CountParameters();
315 			if (paramCount > 0) {
316 				Parameter* parameter = syscall->ParameterAt(0);
317 				if (parameter->AlignmentTypeName()) {
318 					file << "(" << parameter->TypeName() << ")*("
319 						 << parameter->AlignmentTypeName()
320 						 << "*)args";
321 				} else {
322 					file << "*(" << _GetPointerType(parameter->TypeName())
323 						<< ")args";
324 				}
325 				for (int k = 1; k < paramCount; k++) {
326 					parameter = syscall->ParameterAt(k);
327 					if (parameter->AlignmentTypeName()) {
328 						file << ", (" << parameter->TypeName() << ")*("
329 							<< parameter->AlignmentTypeName()
330 							<< "*)((char*)args + " << parameter->Offset()
331 							<< ")";
332 					} else {
333 						file << ", *(" << _GetPointerType(parameter->TypeName())
334 							<< ")((char*)args + " << parameter->Offset()
335 							<< ")";
336 					}
337 				}
338 			}
339 			file << ");" << endl;
340 			file << "\tbreak;" << endl;
341 		}
342 	}
343 
344 	void _WriteNumbersFile(const char *filename)
345 	{
346 		// open the syscall numbers output file
347 		ofstream file(filename, ofstream::out | ofstream::trunc);
348 		if (!file.is_open())
349 			throw IOException(string("Failed to open `") + filename + "'.");
350 
351 		// output the defines
352 		const char *prefix = "_user_";
353 		size_t prefixLen = strlen(prefix);
354 		for (int i = 0; i < fSyscallCount; i++) {
355 			const Syscall* syscall = fSyscallVector->SyscallAt(i);
356 			string name(syscall->KernelName());
357 
358 			// drop the leading "_user_" prefix
359 			if (name.find(prefix) != 0)
360 				throw Exception(string("Bad kernel name: `") + name + "'.");
361 			name = string(name, prefixLen);
362 
363 			// convert to upper case (is there no function for that?)
364 			string defineName;
365 			for (int k = 0; k < (int)name.length(); k++)
366 				defineName += toupper(name[k]);
367 			file << "#define SYSCALL_" << defineName << " " << i << endl;
368 		}
369 	}
370 
371 	void _WriteTableFile(const char *filename)
372 	{
373 		// open the syscall table output file
374 		ofstream file(filename, ofstream::out | ofstream::trunc);
375 		if (!file.is_open())
376 			throw IOException(string("Failed to open `") + filename + "'.");
377 
378 		// output syscall count macro
379 		file << "#define SYSCALL_COUNT " << fSyscallCount << endl;
380 		file << endl;
381 
382 		// assembler guard
383 		file << "#ifndef _ASSEMBLER" << endl;
384 		file << endl;
385 
386 		// output syscall count
387 		file << "const int kSyscallCount = SYSCALL_COUNT;" << endl;
388 		file << endl;
389 
390 		// syscall infos array preamble
391 		file << "const syscall_info kSyscallInfos[] = {" << endl;
392 
393 		// the syscall infos
394 		for (int i = 0; i < fSyscallCount; i++) {
395 			const Syscall* syscall = fSyscallVector->SyscallAt(i);
396 
397 			// get the parameter size
398 			int paramSize = 0;
399 			if (Parameter* parameter = syscall->LastParameter())
400 				paramSize = parameter->Offset() + parameter->UsedSize();
401 
402 			// output the info for the syscall
403 			file << "\t{ (void *)" << syscall->KernelName() << ", "
404 				<< paramSize << " }," << endl;
405 		}
406 
407 		// syscall infos array end
408 		file << "};" << endl;
409 		file << endl;
410 
411 		// syscall parameters infos array preamble
412 		file << "const extended_syscall_info kExtendedSyscallInfos[] = {"
413 			<< endl;
414 
415 		// the syscall parameters infos
416 		for (int i = 0; i < fSyscallCount; i++) {
417 			const Syscall* syscall = fSyscallVector->SyscallAt(i);
418 			int paramCount = syscall->CountParameters();
419 
420 			file << "\t{" << endl;
421 			file << "\t\t\"" << syscall->Name() << "\", " << paramCount << ","
422 				<< endl;
423 
424 			// return type
425 			Type* returnType = syscall->ReturnType();
426 			file << "\t\t{ " << returnType->Size() << ", "
427 				<< returnType->UsedSize() << ", "
428 				<< _GetTypeCode(returnType) << " }," << endl;
429 
430 			// parameters
431 			file << "\t\t{" << endl;
432 
433 			for (int k = 0; k < paramCount; k++) {
434 				const Parameter* parameter = syscall->ParameterAt(k);
435 				file << "\t\t\t{ " << parameter->Offset() << ", "
436 					<< parameter->Size() << ", "
437 					<< parameter->UsedSize() << ", "
438 					<< _GetTypeCode(parameter) << " }," << endl;
439 			}
440 
441 			file << "\t\t}" << endl;
442 			file << "\t}," << endl;
443 		}
444 
445 		// syscall parameters infos array end
446 		file << "};" << endl;
447 
448 		// assembler guard end
449 		file << "#endif	// _ASSEMBLER" << endl;
450 	}
451 
452 	void _WriteSTraceFile(const char *filename)
453 	{
454 		// open the syscall table output file
455 		ofstream file(filename, ofstream::out | ofstream::trunc);
456 		if (!file.is_open())
457 			throw IOException(string("Failed to open `") + filename + "'.");
458 
459 		// the file defines a single function get_syscalls
460 		file << "void" << endl
461 			<< "GET_SYSCALLS(vector<Syscall*> &syscalls)" << endl
462 			<< "{" << endl
463 			<< "\tSyscall *syscall;" << endl
464 			<< "\tTypeHandler *handler;" << endl
465 			<< "(void)syscall;" << endl
466 			<< "(void)handler;" << endl;
467 
468 		int chunkSize = (fSyscallCount + 19) / 20;
469 
470 		// iterate through the syscalls
471 		for (int i = 0; i < fSyscallCount; i++) {
472 			const Syscall* syscall = fSyscallVector->SyscallAt(i);
473 
474 			if (i % chunkSize == 0) {
475 				// chunk end
476 				file << endl;
477 				if (i > 0)
478 					file << "#endif" << endl;
479 				// chunk begin
480 				file << "#ifdef SYSCALLS_CHUNK_" << (i / chunkSize) << endl;
481 			}
482 
483 			// spacing, comment
484 			file << endl;
485 			file << "\t// " << syscall->Name() << endl;
486 
487 			// create the return type handler
488 			const char* returnType = syscall->ReturnType()->TypeName();
489 			file << "\thandler = TypeHandlerFactory<"
490 				<< returnType
491 				<< ">::Create();" << endl;
492 
493 			// create the syscall
494 			file << "\tsyscall = new Syscall(\"" << syscall->Name() << "\", "
495 				<< "\"" << returnType << "\", "<< "handler);" << endl;
496 			file << "\tsyscalls.push_back(syscall);" << endl;
497 
498 			// add the parameters
499 			int paramCount = syscall->CountParameters();
500 			for (int k = 0; k < paramCount; k++) {
501 				const Parameter* parameter = syscall->ParameterAt(k);
502 
503 				// create the parameter type handler
504 				file << "\thandler = TypeHandlerFactory<"
505 					<< parameter->TypeName() << ">::Create();" << endl;
506 
507 				// add the parameter
508 				file << "\tsyscall->AddParameter(\""
509 					<< parameter->ParameterName() << "\", "
510 					<< parameter->Offset() << ", \"" << parameter->TypeName()
511 					<< "\", handler);" << endl;
512 			}
513 		}
514 
515 		// last syscall chunk end
516 		file << "#endif" << endl;
517 
518 		// function end
519 		file << "}" << endl;
520 	}
521 
522 	static string _GetPointerType(const char *type)
523 	{
524 		char *parenthesis = strchr(type, ')');
525 		if (!parenthesis)
526 			return string(type) + "*";
527 		// function pointer type
528 		return string(type, parenthesis - type) + "*" + parenthesis;
529 	}
530 
531 	static string _GetTypeCode(const Type* type)
532 	{
533 		const char* typeName = type->TypeName();
534 		if (strchr(typeName, '*')) {
535 			// pointer type
536 			// check, if it is a string constant ("const char *" or
537 			// "char const *")
538 			if (_GetTypeCodeTokenize(typeName) == "const"
539 					&& _GetTypeCodeTokenize(typeName) == "char"
540 					&& _GetTypeCodeTokenize(typeName) == "*"
541 					&& _GetTypeCodeTokenize(typeName) == ""
542 				|| _GetTypeCodeTokenize(typeName) == "char"
543 					&& _GetTypeCodeTokenize(typeName) == "const"
544 					&& _GetTypeCodeTokenize(typeName) == "*"
545 					&& _GetTypeCodeTokenize(typeName) == "") {
546 				return "B_STRING_TYPE";
547 			}
548 
549 			// not a string constant
550 			return "B_POINTER_TYPE";
551 		} else {
552 			// non-pointer type
553 			switch (type->Size()) {
554 				case 1:
555 					return "B_INT8_TYPE";
556 				case 2:
557 					return "B_INT16_TYPE";
558 				case 4:
559 					return "B_INT32_TYPE";
560 				case 8:
561 					return "B_INT64_TYPE";
562 				default:
563 					return "B_RAW_TYPE";
564 			}
565 		}
566 	}
567 
568 	static string _GetTypeCodeTokenize(const char*& type)
569 	{
570 		// skip whitespace
571 		while (*type != '\0' && isspace(*type))
572 			type++;
573 
574 		switch (*type) {
575 			case '\0':
576 				return "";
577 
578 			case '*':
579 			case '(':
580 			case ')':
581 			case '&':
582 				return string(type++, 1);
583 
584 			default:
585 			{
586 				if (*type != '_' && !isalpha(*type)) {
587 					// probably invalid, just return something
588 					return string(type++, 1);
589 				}
590 
591 				// an identifier
592 				const char* start = type;
593 				while (*type == '_' || isalnum(*type))
594 					type++;
595 				return string(start, type - start);
596 			}
597 		}
598 	}
599 
600 private:
601 	SyscallVector*	fSyscallVector;
602 	int				fSyscallCount;
603 };
604 
605 // main
606 int
607 main(int argc, char **argv)
608 {
609 	try {
610 		return Main().Run(argc, argv);
611 	} catch (Exception &exception) {
612 		fprintf(stderr, "%s\n", exception.what());
613 		return 1;
614 	}
615 }
616