xref: /haiku/src/tools/gensyscalls/gensyscalls.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 "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 	// take care of extra alignment for long parameters
170 	// this is needed to sort out parameter offsets on ARM
171 	if (size >= kLongParameterAlignmentSize) {
172 		if ((offset % kLongParameterAlignmentSize) != 0) {
173 			offset += kLongParameterAlignmentSize
174 				- offset % kLongParameterAlignmentSize;
175 		}
176 	}
177 
178 	int usedSize = (size + kParameterAlignmentSize - 1)
179 		/ kParameterAlignmentSize * kParameterAlignmentSize;
180 	const char* alignmentType
181 		= size != usedSize && size < kParameterAlignmentSize
182 			? kParameterAlignmentType : 0;
183 
184 	AddParameter(typeName, parameterName, size, usedSize, offset,
185 		alignmentType);
186 }
187 
188 
189 // #pragma mark - SyscallVector
190 
191 
192 struct SyscallVector::_SyscallVector : public vector<Syscall*> {
193 };
194 
195 
196 
197 SyscallVector::SyscallVector()
198 	:
199 	fSyscalls(new _SyscallVector)
200 {
201 }
202 
203 
204 SyscallVector::~SyscallVector()
205 {
206 	int count = CountSyscalls();
207 	for (int i = 0; i < count; i++)
208 		delete SyscallAt(i);
209 	delete fSyscalls;
210 }
211 
212 
213 SyscallVector*
214 SyscallVector::Create()
215 {
216 	return new SyscallVector;
217 }
218 
219 
220 int
221 SyscallVector::CountSyscalls() const
222 {
223 	return fSyscalls->size();
224 }
225 
226 
227 Syscall*
228 SyscallVector::SyscallAt(int index) const
229 {
230 	if (index < 0 || index >= CountSyscalls())
231 		return NULL;
232 
233 	return (*fSyscalls)[index];
234 }
235 
236 
237 Syscall*
238 SyscallVector::CreateSyscall(const char* name, const char* kernelName)
239 {
240 	Syscall* syscall = new Syscall(name, kernelName);
241 	fSyscalls->push_back(syscall);
242 	return syscall;
243 }
244 
245 
246 // #pragma mark -
247 
248 
249 class Main {
250 public:
251 	int Run(int argc, char** argv)
252 	{
253 		// parse arguments
254 		const char* syscallsFile = NULL;
255 		const char* dispatcherFile = NULL;
256 		const char* numbersFile = NULL;
257 		const char* tableFile = NULL;
258 		const char* straceFile = NULL;
259 
260 		for (int argi = 1; argi < argc; argi++) {
261 			string arg(argv[argi]);
262 			if (arg == "-h" || arg == "--help") {
263 				print_usage(false);
264 				return 0;
265 			} else if (arg == "-c") {
266 				if (argi + 1 >= argc) {
267 					print_usage(true);
268 					return 1;
269 				}
270 				syscallsFile = argv[++argi];
271 			} else if (arg == "-d") {
272 				if (argi + 1 >= argc) {
273 					print_usage(true);
274 					return 1;
275 				}
276 				dispatcherFile = argv[++argi];
277 			} else if (arg == "-n") {
278 				if (argi + 1 >= argc) {
279 					print_usage(true);
280 					return 1;
281 				}
282 				numbersFile = argv[++argi];
283 			} else if (arg == "-t") {
284 				if (argi + 1 >= argc) {
285 					print_usage(true);
286 					return 1;
287 				}
288 				tableFile = argv[++argi];
289 			} else if (arg == "-s") {
290 				if (argi + 1 >= argc) {
291 					print_usage(true);
292 					return 1;
293 				}
294 				straceFile = argv[++argi];
295 			} else {
296 				print_usage(true);
297 				return 1;
298 			}
299 		}
300 		fSyscallVector = create_syscall_vector();
301 		fSyscallCount = fSyscallVector->CountSyscalls();
302 		if (!syscallsFile && !dispatcherFile && !numbersFile && !tableFile
303 			&& !straceFile) {
304 			printf("Found %d syscalls.\n", fSyscallCount);
305 			return 0;
306 		}
307 		// generate the output
308 		if (syscallsFile)
309 			_WriteSyscallsFile(syscallsFile);
310 		if (dispatcherFile)
311 			_WriteDispatcherFile(dispatcherFile);
312 		if (numbersFile)
313 			_WriteNumbersFile(numbersFile);
314 		if (tableFile)
315 			_WriteTableFile(tableFile);
316 		if (straceFile)
317 			_WriteSTraceFile(straceFile);
318 		return 0;
319 	}
320 
321 	void _WriteSyscallsFile(const char* filename)
322 	{
323 		// open the syscalls output file
324 		ofstream file(filename, ofstream::out | ofstream::trunc);
325 		if (!file.is_open())
326 			throw IOException(string("Failed to open `") + filename + "'.");
327 
328 		// output the syscalls definitions
329 		for (int i = 0; i < fSyscallCount; i++) {
330 			const Syscall* syscall = fSyscallVector->SyscallAt(i);
331 			int paramCount = syscall->CountParameters();
332 			int paramSize = 0;
333 			// XXX: Currently the SYSCALL macros support 4 byte aligned
334 			// parameters only. This has to change, of course.
335 			for (int k = 0; k < paramCount; k++) {
336 				const Parameter* parameter = syscall->ParameterAt(k);
337 				int size = parameter->UsedSize();
338 				paramSize += (size + 3) / 4 * 4;
339 			}
340 			file << "SYSCALL" << (paramSize / 4) << "("
341 				<< syscall->Name() << ", " << i << ")" << endl;
342 		}
343 	}
344 
345 	void _WriteDispatcherFile(const char* filename)
346 	{
347 		// open the dispatcher output file
348 		ofstream file(filename, ofstream::out | ofstream::trunc);
349 		if (!file.is_open())
350 			throw IOException(string("Failed to open `") + filename + "'.");
351 
352 		// output the case statements
353 		for (int i = 0; i < fSyscallCount; i++) {
354 			const Syscall* syscall = fSyscallVector->SyscallAt(i);
355 			file << "case " << i << ":" << endl;
356 			file << "\t";
357 			if (string(syscall->ReturnType()->TypeName()) != "void")
358 				file << "*_returnValue = ";
359 			file << syscall->KernelName() << "(";
360 			int paramCount = syscall->CountParameters();
361 			if (paramCount > 0) {
362 				Parameter* parameter = syscall->ParameterAt(0);
363 				if (parameter->AlignmentTypeName()) {
364 					file << "(" << parameter->TypeName() << ")*("
365 						 << parameter->AlignmentTypeName()
366 						 << "*)args";
367 				} else {
368 					file << "*(" << _GetPointerType(parameter->TypeName())
369 						<< ")args";
370 				}
371 				for (int k = 1; k < paramCount; k++) {
372 					parameter = syscall->ParameterAt(k);
373 					if (parameter->AlignmentTypeName()) {
374 						file << ", (" << parameter->TypeName() << ")*("
375 							<< parameter->AlignmentTypeName()
376 							<< "*)((char*)args + " << parameter->Offset()
377 							<< ")";
378 					} else {
379 						file << ", *(" << _GetPointerType(parameter->TypeName())
380 							<< ")((char*)args + " << parameter->Offset()
381 							<< ")";
382 					}
383 				}
384 			}
385 			file << ");" << endl;
386 			file << "\tbreak;" << endl;
387 		}
388 	}
389 
390 	void _WriteNumbersFile(const char* filename)
391 	{
392 		// open the syscall numbers output file
393 		ofstream file(filename, ofstream::out | ofstream::trunc);
394 		if (!file.is_open())
395 			throw IOException(string("Failed to open `") + filename + "'.");
396 
397 		// output the defines
398 		const char* prefix = "_user_";
399 		size_t prefixLen = strlen(prefix);
400 		for (int i = 0; i < fSyscallCount; i++) {
401 			const Syscall* syscall = fSyscallVector->SyscallAt(i);
402 			string name(syscall->KernelName());
403 
404 			// drop the leading "_user_" prefix
405 			if (name.find(prefix) != 0)
406 				throw Exception(string("Bad kernel name: `") + name + "'.");
407 			name = string(name, prefixLen);
408 
409 			// convert to upper case (is there no function for that?)
410 			string defineName;
411 			for (int k = 0; k < (int)name.length(); k++)
412 				defineName += toupper(name[k]);
413 			file << "#define SYSCALL_" << defineName << " " << i << endl;
414 		}
415 	}
416 
417 	void _WriteTableFile(const char* filename)
418 	{
419 		// open the syscall table output file
420 		ofstream file(filename, ofstream::out | ofstream::trunc);
421 		if (!file.is_open())
422 			throw IOException(string("Failed to open `") + filename + "'.");
423 
424 		// output syscall count macro
425 		file << "#define SYSCALL_COUNT " << fSyscallCount << endl;
426 		file << endl;
427 
428 		// assembler guard
429 		file << "#ifndef _ASSEMBLER" << endl;
430 		file << endl;
431 
432 		// includes
433 		file << "#include <TypeConstants.h>" << endl;
434 		file << endl;
435 
436 		// output syscall count
437 		file << "const int kSyscallCount = SYSCALL_COUNT;" << endl;
438 		file << endl;
439 
440 		// syscall infos array preamble
441 		file << "const syscall_info kSyscallInfos[] = {" << endl;
442 
443 		// the syscall infos
444 		for (int i = 0; i < fSyscallCount; i++) {
445 			const Syscall* syscall = fSyscallVector->SyscallAt(i);
446 
447 			// get the parameter size
448 			int paramSize = 0;
449 			if (Parameter* parameter = syscall->LastParameter())
450 				paramSize = parameter->Offset() + parameter->UsedSize();
451 
452 			// output the info for the syscall
453 			file << "\t{ (void *)" << syscall->KernelName() << ", "
454 				<< paramSize << " }," << endl;
455 		}
456 
457 		// syscall infos array end
458 		file << "};" << endl;
459 		file << endl;
460 
461 		// syscall parameters infos array preamble
462 		file << "const extended_syscall_info kExtendedSyscallInfos[] = {"
463 			<< endl;
464 
465 		// the syscall parameters infos
466 		for (int i = 0; i < fSyscallCount; i++) {
467 			const Syscall* syscall = fSyscallVector->SyscallAt(i);
468 			int paramCount = syscall->CountParameters();
469 
470 			file << "\t{" << endl;
471 			file << "\t\t\"" << syscall->Name() << "\", " << paramCount << ","
472 				<< endl;
473 
474 			// return type
475 			Type* returnType = syscall->ReturnType();
476 			file << "\t\t{ " << returnType->Size() << ", "
477 				<< returnType->UsedSize() << ", "
478 				<< _GetTypeCode(returnType) << " }," << endl;
479 
480 			// parameters
481 			file << "\t\t{" << endl;
482 
483 			for (int k = 0; k < paramCount; k++) {
484 				const Parameter* parameter = syscall->ParameterAt(k);
485 				file << "\t\t\t{ " << parameter->Offset() << ", "
486 					<< parameter->Size() << ", "
487 					<< parameter->UsedSize() << ", "
488 					<< _GetTypeCode(parameter) << " }," << endl;
489 			}
490 
491 			file << "\t\t}" << endl;
492 			file << "\t}," << endl;
493 		}
494 
495 		// syscall parameters infos array end
496 		file << "};" << endl;
497 
498 		// assembler guard end
499 		file << "#endif	// _ASSEMBLER" << endl;
500 	}
501 
502 	void _WriteSTraceFile(const char* filename)
503 	{
504 		// open the syscall table output file
505 		ofstream file(filename, ofstream::out | ofstream::trunc);
506 		if (!file.is_open())
507 			throw IOException(string("Failed to open `") + filename + "'.");
508 
509 		// the file defines a single function get_syscalls
510 		file << "void" << endl
511 			<< "GET_SYSCALLS(vector<Syscall*> &syscalls)" << endl
512 			<< "{" << endl
513 			<< "\tSyscall *syscall;" << endl
514 			<< "\tTypeHandler *handler;" << endl
515 			<< "(void)syscall;" << endl
516 			<< "(void)handler;" << endl;
517 
518 		int chunkSize = (fSyscallCount + 19) / 20;
519 
520 		// iterate through the syscalls
521 		for (int i = 0; i < fSyscallCount; i++) {
522 			const Syscall* syscall = fSyscallVector->SyscallAt(i);
523 
524 			if (i % chunkSize == 0) {
525 				// chunk end
526 				file << endl;
527 				if (i > 0)
528 					file << "#endif" << endl;
529 				// chunk begin
530 				file << "#ifdef SYSCALLS_CHUNK_" << (i / chunkSize) << endl;
531 			}
532 
533 			// spacing, comment
534 			file << endl;
535 			file << "\t// " << syscall->Name() << endl;
536 
537 			// create the return type handler
538 			const char* returnType = syscall->ReturnType()->TypeName();
539 			file << "\thandler = TypeHandlerFactory<"
540 				<< returnType
541 				<< ">::Create();" << endl;
542 
543 			// create the syscall
544 			file << "\tsyscall = new Syscall(\"" << syscall->Name() << "\", "
545 				<< "\"" << returnType << "\", "<< "handler);" << endl;
546 			file << "\tsyscalls.push_back(syscall);" << endl;
547 
548 			// add the parameters
549 			int paramCount = syscall->CountParameters();
550 			for (int k = 0; k < paramCount; k++) {
551 				const Parameter* parameter = syscall->ParameterAt(k);
552 
553 				// create the parameter type handler
554 				file << "\thandler = TypeHandlerFactory<"
555 					<< parameter->TypeName() << ">::Create();" << endl;
556 
557 				// add the parameter
558 				file << "\tsyscall->AddParameter(\""
559 					<< parameter->ParameterName() << "\", "
560 					<< parameter->Offset() << ", \"" << parameter->TypeName()
561 					<< "\", handler);" << endl;
562 			}
563 		}
564 
565 		// last syscall chunk end
566 		file << "#endif" << endl;
567 
568 		// function end
569 		file << "}" << endl;
570 	}
571 
572 	static string _GetPointerType(const char* type)
573 	{
574 		const char* parenthesis = strchr(type, ')');
575 		if (!parenthesis)
576 			return string(type) + "*";
577 		// function pointer type
578 		return string(type, parenthesis - type) + "*" + parenthesis;
579 	}
580 
581 	static string _GetTypeCode(const Type* type)
582 	{
583 		const char* typeName = type->TypeName();
584 		if (strchr(typeName, '*')) {
585 			// pointer type
586 			// check, if it is a string constant ("const char *" or
587 			// "char const *")
588 			if ((_GetTypeCodeTokenize(typeName) == "const"
589 					&& _GetTypeCodeTokenize(typeName) == "char"
590 					&& _GetTypeCodeTokenize(typeName) == "*"
591 					&& _GetTypeCodeTokenize(typeName) == "")
592 				|| (_GetTypeCodeTokenize(typeName) == "char"
593 					&& _GetTypeCodeTokenize(typeName) == "const"
594 					&& _GetTypeCodeTokenize(typeName) == "*"
595 					&& _GetTypeCodeTokenize(typeName) == "")) {
596 				return "B_STRING_TYPE";
597 			}
598 
599 			// not a string constant
600 			return "B_POINTER_TYPE";
601 		} else {
602 			// non-pointer type
603 			switch (type->Size()) {
604 				case 1:
605 					return "B_INT8_TYPE";
606 				case 2:
607 					return "B_INT16_TYPE";
608 				case 4:
609 					return "B_INT32_TYPE";
610 				case 8:
611 					return "B_INT64_TYPE";
612 				default:
613 					return "B_RAW_TYPE";
614 			}
615 		}
616 	}
617 
618 	static string _GetTypeCodeTokenize(const char*& type)
619 	{
620 		// skip whitespace
621 		while (*type != '\0' && isspace(*type))
622 			type++;
623 
624 		switch (*type) {
625 			case '\0':
626 				return "";
627 
628 			case '*':
629 			case '(':
630 			case ')':
631 			case '&':
632 				return string(type++, 1);
633 
634 			default:
635 			{
636 				if (*type != '_' && !isalpha(*type)) {
637 					// probably invalid, just return something
638 					return string(type++, 1);
639 				}
640 
641 				// an identifier
642 				const char* start = type;
643 				while (*type == '_' || isalnum(*type))
644 					type++;
645 				return string(start, type - start);
646 			}
647 		}
648 	}
649 
650 private:
651 	SyscallVector*	fSyscallVector;
652 	int				fSyscallCount;
653 };
654 
655 
656 int
657 main(int argc, char** argv)
658 {
659 	try {
660 		return Main().Run(argc, argv);
661 	} catch (Exception &exception) {
662 		fprintf(stderr, "%s\n", exception.what());
663 		return 1;
664 	}
665 }
666