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