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