xref: /haiku/src/tools/gensyscalls/gensyscalls.cpp (revision 6490c5b6211c94ec48c03c12b90e8603fe268d1b)
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 		// output syscall count
424 		file << "const int kSyscallCount = SYSCALL_COUNT;" << endl;
425 		file << endl;
426 
427 		// syscall infos array preamble
428 		file << "const syscall_info kSyscallInfos[] = {" << endl;
429 
430 		// the syscall infos
431 		for (int i = 0; i < fSyscallCount; i++) {
432 			const Syscall* syscall = fSyscallVector->SyscallAt(i);
433 
434 			// get the parameter size
435 			int paramSize = 0;
436 			if (Parameter* parameter = syscall->LastParameter())
437 				paramSize = parameter->Offset() + parameter->UsedSize();
438 
439 			// output the info for the syscall
440 			file << "\t{ (void *)" << syscall->KernelName() << ", "
441 				<< paramSize << " }," << endl;
442 		}
443 
444 		// syscall infos array end
445 		file << "};" << endl;
446 		file << endl;
447 
448 		// syscall parameters infos array preamble
449 		file << "const extended_syscall_info kExtendedSyscallInfos[] = {"
450 			<< endl;
451 
452 		// the syscall parameters infos
453 		for (int i = 0; i < fSyscallCount; i++) {
454 			const Syscall* syscall = fSyscallVector->SyscallAt(i);
455 			int paramCount = syscall->CountParameters();
456 
457 			file << "\t{" << endl;
458 			file << "\t\t\"" << syscall->Name() << "\", " << paramCount << ","
459 				<< endl;
460 
461 			// return type
462 			Type* returnType = syscall->ReturnType();
463 			file << "\t\t{ " << returnType->Size() << ", "
464 				<< returnType->UsedSize() << ", "
465 				<< _GetTypeCode(returnType) << " }," << endl;
466 
467 			// parameters
468 			file << "\t\t{" << endl;
469 
470 			for (int k = 0; k < paramCount; k++) {
471 				const Parameter* parameter = syscall->ParameterAt(k);
472 				file << "\t\t\t{ " << parameter->Offset() << ", "
473 					<< parameter->Size() << ", "
474 					<< parameter->UsedSize() << ", "
475 					<< _GetTypeCode(parameter) << " }," << endl;
476 			}
477 
478 			file << "\t\t}" << endl;
479 			file << "\t}," << endl;
480 		}
481 
482 		// syscall parameters infos array end
483 		file << "};" << endl;
484 
485 		// assembler guard end
486 		file << "#endif	// _ASSEMBLER" << endl;
487 	}
488 
489 	void _WriteSTraceFile(const char* filename)
490 	{
491 		// open the syscall table output file
492 		ofstream file(filename, ofstream::out | ofstream::trunc);
493 		if (!file.is_open())
494 			throw IOException(string("Failed to open `") + filename + "'.");
495 
496 		// the file defines a single function get_syscalls
497 		file << "void" << endl
498 			<< "GET_SYSCALLS(vector<Syscall*> &syscalls)" << endl
499 			<< "{" << endl
500 			<< "\tSyscall *syscall;" << endl
501 			<< "\tTypeHandler *handler;" << endl
502 			<< "(void)syscall;" << endl
503 			<< "(void)handler;" << endl;
504 
505 		int chunkSize = (fSyscallCount + 19) / 20;
506 
507 		// iterate through the syscalls
508 		for (int i = 0; i < fSyscallCount; i++) {
509 			const Syscall* syscall = fSyscallVector->SyscallAt(i);
510 
511 			if (i % chunkSize == 0) {
512 				// chunk end
513 				file << endl;
514 				if (i > 0)
515 					file << "#endif" << endl;
516 				// chunk begin
517 				file << "#ifdef SYSCALLS_CHUNK_" << (i / chunkSize) << endl;
518 			}
519 
520 			// spacing, comment
521 			file << endl;
522 			file << "\t// " << syscall->Name() << endl;
523 
524 			// create the return type handler
525 			const char* returnType = syscall->ReturnType()->TypeName();
526 			file << "\thandler = TypeHandlerFactory<"
527 				<< returnType
528 				<< ">::Create();" << endl;
529 
530 			// create the syscall
531 			file << "\tsyscall = new Syscall(\"" << syscall->Name() << "\", "
532 				<< "\"" << returnType << "\", "<< "handler);" << endl;
533 			file << "\tsyscalls.push_back(syscall);" << endl;
534 
535 			// add the parameters
536 			int paramCount = syscall->CountParameters();
537 			for (int k = 0; k < paramCount; k++) {
538 				const Parameter* parameter = syscall->ParameterAt(k);
539 
540 				// create the parameter type handler
541 				file << "\thandler = TypeHandlerFactory<"
542 					<< parameter->TypeName() << ">::Create();" << endl;
543 
544 				// add the parameter
545 				file << "\tsyscall->AddParameter(\""
546 					<< parameter->ParameterName() << "\", "
547 					<< parameter->Offset() << ", \"" << parameter->TypeName()
548 					<< "\", handler);" << endl;
549 			}
550 		}
551 
552 		// last syscall chunk end
553 		file << "#endif" << endl;
554 
555 		// function end
556 		file << "}" << endl;
557 	}
558 
559 	static string _GetPointerType(const char* type)
560 	{
561 		const char* parenthesis = strchr(type, ')');
562 		if (!parenthesis)
563 			return string(type) + "*";
564 		// function pointer type
565 		return string(type, parenthesis - type) + "*" + parenthesis;
566 	}
567 
568 	static string _GetTypeCode(const Type* type)
569 	{
570 		const char* typeName = type->TypeName();
571 		if (strchr(typeName, '*')) {
572 			// pointer type
573 			// check, if it is a string constant ("const char *" or
574 			// "char const *")
575 			if ((_GetTypeCodeTokenize(typeName) == "const"
576 					&& _GetTypeCodeTokenize(typeName) == "char"
577 					&& _GetTypeCodeTokenize(typeName) == "*"
578 					&& _GetTypeCodeTokenize(typeName) == "")
579 				|| (_GetTypeCodeTokenize(typeName) == "char"
580 					&& _GetTypeCodeTokenize(typeName) == "const"
581 					&& _GetTypeCodeTokenize(typeName) == "*"
582 					&& _GetTypeCodeTokenize(typeName) == "")) {
583 				return "B_STRING_TYPE";
584 			}
585 
586 			// not a string constant
587 			return "B_POINTER_TYPE";
588 		} else {
589 			// non-pointer type
590 			switch (type->Size()) {
591 				case 1:
592 					return "B_INT8_TYPE";
593 				case 2:
594 					return "B_INT16_TYPE";
595 				case 4:
596 					return "B_INT32_TYPE";
597 				case 8:
598 					return "B_INT64_TYPE";
599 				default:
600 					return "B_RAW_TYPE";
601 			}
602 		}
603 	}
604 
605 	static string _GetTypeCodeTokenize(const char*& type)
606 	{
607 		// skip whitespace
608 		while (*type != '\0' && isspace(*type))
609 			type++;
610 
611 		switch (*type) {
612 			case '\0':
613 				return "";
614 
615 			case '*':
616 			case '(':
617 			case ')':
618 			case '&':
619 				return string(type++, 1);
620 
621 			default:
622 			{
623 				if (*type != '_' && !isalpha(*type)) {
624 					// probably invalid, just return something
625 					return string(type++, 1);
626 				}
627 
628 				// an identifier
629 				const char* start = type;
630 				while (*type == '_' || isalnum(*type))
631 					type++;
632 				return string(start, type - start);
633 			}
634 		}
635 	}
636 
637 private:
638 	SyscallVector*	fSyscallVector;
639 	int				fSyscallCount;
640 };
641 
642 
643 int
644 main(int argc, char** argv)
645 {
646 	try {
647 		return Main().Run(argc, argv);
648 	} catch (Exception &exception) {
649 		fprintf(stderr, "%s\n", exception.what());
650 		return 1;
651 	}
652 }
653