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