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