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
18using std::vector;
19
20
21// print_usage
22static void
23print_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
53Type::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
67Parameter::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
80struct Syscall::ParameterVector : public vector<Parameter*> {
81};
82
83
84Syscall::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
94Syscall::~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
105int
106Syscall::CountParameters() const
107{
108	return fParameters->size();
109}
110
111
112Parameter*
113Syscall::ParameterAt(int index) const
114{
115	if (index < 0 || index >= CountParameters())
116		return NULL;
117
118	return (*fParameters)[index];
119}
120
121
122Parameter*
123Syscall::LastParameter() const
124{
125	return ParameterAt(CountParameters() - 1);
126}
127
128
129void
130Syscall::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
142Type*
143Syscall::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
151Parameter*
152Syscall::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
161void
162Syscall::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
183struct SyscallVector::_SyscallVector : public vector<Syscall*> {
184};
185
186
187
188SyscallVector::SyscallVector()
189	:
190	fSyscalls(new _SyscallVector)
191{
192}
193
194
195SyscallVector::~SyscallVector()
196{
197	int count = CountSyscalls();
198	for (int i = 0; i < count; i++)
199		delete SyscallAt(i);
200	delete fSyscalls;
201}
202
203
204SyscallVector*
205SyscallVector::Create()
206{
207	return new SyscallVector;
208}
209
210
211int
212SyscallVector::CountSyscalls() const
213{
214	return fSyscalls->size();
215}
216
217
218Syscall*
219SyscallVector::SyscallAt(int index) const
220{
221	if (index < 0 || index >= CountSyscalls())
222		return NULL;
223
224	return (*fSyscalls)[index];
225}
226
227
228Syscall*
229SyscallVector::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
240class Main {
241public:
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		// includes
424		file << "#include <TypeConstants.h>" << endl;
425		file << endl;
426
427		// output syscall count
428		file << "const int kSyscallCount = SYSCALL_COUNT;" << endl;
429		file << endl;
430
431		// syscall infos array preamble
432		file << "const syscall_info kSyscallInfos[] = {" << endl;
433
434		// the syscall infos
435		for (int i = 0; i < fSyscallCount; i++) {
436			const Syscall* syscall = fSyscallVector->SyscallAt(i);
437
438			// get the parameter size
439			int paramSize = 0;
440			if (Parameter* parameter = syscall->LastParameter())
441				paramSize = parameter->Offset() + parameter->UsedSize();
442
443			// output the info for the syscall
444			file << "\t{ (void *)" << syscall->KernelName() << ", "
445				<< paramSize << " }," << endl;
446		}
447
448		// syscall infos array end
449		file << "};" << endl;
450		file << endl;
451
452		// syscall parameters infos array preamble
453		file << "const extended_syscall_info kExtendedSyscallInfos[] = {"
454			<< endl;
455
456		// the syscall parameters infos
457		for (int i = 0; i < fSyscallCount; i++) {
458			const Syscall* syscall = fSyscallVector->SyscallAt(i);
459			int paramCount = syscall->CountParameters();
460
461			file << "\t{" << endl;
462			file << "\t\t\"" << syscall->Name() << "\", " << paramCount << ","
463				<< endl;
464
465			// return type
466			Type* returnType = syscall->ReturnType();
467			file << "\t\t{ " << returnType->Size() << ", "
468				<< returnType->UsedSize() << ", "
469				<< _GetTypeCode(returnType) << " }," << endl;
470
471			// parameters
472			file << "\t\t{" << endl;
473
474			for (int k = 0; k < paramCount; k++) {
475				const Parameter* parameter = syscall->ParameterAt(k);
476				file << "\t\t\t{ " << parameter->Offset() << ", "
477					<< parameter->Size() << ", "
478					<< parameter->UsedSize() << ", "
479					<< _GetTypeCode(parameter) << " }," << endl;
480			}
481
482			file << "\t\t}" << endl;
483			file << "\t}," << endl;
484		}
485
486		// syscall parameters infos array end
487		file << "};" << endl;
488
489		// assembler guard end
490		file << "#endif	// _ASSEMBLER" << endl;
491	}
492
493	void _WriteSTraceFile(const char* filename)
494	{
495		// open the syscall table output file
496		ofstream file(filename, ofstream::out | ofstream::trunc);
497		if (!file.is_open())
498			throw IOException(string("Failed to open `") + filename + "'.");
499
500		// the file defines a single function get_syscalls
501		file << "void" << endl
502			<< "GET_SYSCALLS(vector<Syscall*> &syscalls)" << endl
503			<< "{" << endl
504			<< "\tSyscall *syscall;" << endl
505			<< "\tTypeHandler *handler;" << endl
506			<< "(void)syscall;" << endl
507			<< "(void)handler;" << endl;
508
509		int chunkSize = (fSyscallCount + 19) / 20;
510
511		// iterate through the syscalls
512		for (int i = 0; i < fSyscallCount; i++) {
513			const Syscall* syscall = fSyscallVector->SyscallAt(i);
514
515			if (i % chunkSize == 0) {
516				// chunk end
517				file << endl;
518				if (i > 0)
519					file << "#endif" << endl;
520				// chunk begin
521				file << "#ifdef SYSCALLS_CHUNK_" << (i / chunkSize) << endl;
522			}
523
524			// spacing, comment
525			file << endl;
526			file << "\t// " << syscall->Name() << endl;
527
528			// create the return type handler
529			const char* returnType = syscall->ReturnType()->TypeName();
530			file << "\thandler = TypeHandlerFactory<"
531				<< returnType
532				<< ">::Create();" << endl;
533
534			// create the syscall
535			file << "\tsyscall = new Syscall(\"" << syscall->Name() << "\", "
536				<< "\"" << returnType << "\", "<< "handler);" << endl;
537			file << "\tsyscalls.push_back(syscall);" << endl;
538
539			// add the parameters
540			int paramCount = syscall->CountParameters();
541			for (int k = 0; k < paramCount; k++) {
542				const Parameter* parameter = syscall->ParameterAt(k);
543
544				// create the parameter type handler
545				file << "\thandler = TypeHandlerFactory<"
546					<< parameter->TypeName() << ">::Create();" << endl;
547
548				// add the parameter
549				file << "\tsyscall->AddParameter(\""
550					<< parameter->ParameterName() << "\", "
551					<< parameter->Offset() << ", \"" << parameter->TypeName()
552					<< "\", handler);" << endl;
553			}
554		}
555
556		// last syscall chunk end
557		file << "#endif" << endl;
558
559		// function end
560		file << "}" << endl;
561	}
562
563	static string _GetPointerType(const char* type)
564	{
565		const char* parenthesis = strchr(type, ')');
566		if (!parenthesis)
567			return string(type) + "*";
568		// function pointer type
569		return string(type, parenthesis - type) + "*" + parenthesis;
570	}
571
572	static string _GetTypeCode(const Type* type)
573	{
574		const char* typeName = type->TypeName();
575		if (strchr(typeName, '*')) {
576			// pointer type
577			// check, if it is a string constant ("const char *" or
578			// "char const *")
579			if ((_GetTypeCodeTokenize(typeName) == "const"
580					&& _GetTypeCodeTokenize(typeName) == "char"
581					&& _GetTypeCodeTokenize(typeName) == "*"
582					&& _GetTypeCodeTokenize(typeName) == "")
583				|| (_GetTypeCodeTokenize(typeName) == "char"
584					&& _GetTypeCodeTokenize(typeName) == "const"
585					&& _GetTypeCodeTokenize(typeName) == "*"
586					&& _GetTypeCodeTokenize(typeName) == "")) {
587				return "B_STRING_TYPE";
588			}
589
590			// not a string constant
591			return "B_POINTER_TYPE";
592		} else {
593			// non-pointer type
594			switch (type->Size()) {
595				case 1:
596					return "B_INT8_TYPE";
597				case 2:
598					return "B_INT16_TYPE";
599				case 4:
600					return "B_INT32_TYPE";
601				case 8:
602					return "B_INT64_TYPE";
603				default:
604					return "B_RAW_TYPE";
605			}
606		}
607	}
608
609	static string _GetTypeCodeTokenize(const char*& type)
610	{
611		// skip whitespace
612		while (*type != '\0' && isspace(*type))
613			type++;
614
615		switch (*type) {
616			case '\0':
617				return "";
618
619			case '*':
620			case '(':
621			case ')':
622			case '&':
623				return string(type++, 1);
624
625			default:
626			{
627				if (*type != '_' && !isalpha(*type)) {
628					// probably invalid, just return something
629					return string(type++, 1);
630				}
631
632				// an identifier
633				const char* start = type;
634				while (*type == '_' || isalnum(*type))
635					type++;
636				return string(start, type - start);
637			}
638		}
639	}
640
641private:
642	SyscallVector*	fSyscallVector;
643	int				fSyscallCount;
644};
645
646
647int
648main(int argc, char** argv)
649{
650	try {
651		return Main().Run(argc, argv);
652	} catch (Exception &exception) {
653		fprintf(stderr, "%s\n", exception.what());
654		return 1;
655	}
656}
657