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	// take care of extra alignment for long parameters
170	// this is needed to sort out parameter offsets on ARM
171	if (size >= kLongParameterAlignmentSize) {
172		if ((offset % kLongParameterAlignmentSize) != 0) {
173			offset += kLongParameterAlignmentSize
174				- offset % kLongParameterAlignmentSize;
175		}
176	}
177
178	int usedSize = (size + kParameterAlignmentSize - 1)
179		/ kParameterAlignmentSize * kParameterAlignmentSize;
180	const char* alignmentType
181		= size != usedSize && size < kParameterAlignmentSize
182			? kParameterAlignmentType : 0;
183
184	AddParameter(typeName, parameterName, size, usedSize, offset,
185		alignmentType);
186}
187
188
189// #pragma mark - SyscallVector
190
191
192struct SyscallVector::_SyscallVector : public vector<Syscall*> {
193};
194
195
196
197SyscallVector::SyscallVector()
198	:
199	fSyscalls(new _SyscallVector)
200{
201}
202
203
204SyscallVector::~SyscallVector()
205{
206	int count = CountSyscalls();
207	for (int i = 0; i < count; i++)
208		delete SyscallAt(i);
209	delete fSyscalls;
210}
211
212
213SyscallVector*
214SyscallVector::Create()
215{
216	return new SyscallVector;
217}
218
219
220int
221SyscallVector::CountSyscalls() const
222{
223	return fSyscalls->size();
224}
225
226
227Syscall*
228SyscallVector::SyscallAt(int index) const
229{
230	if (index < 0 || index >= CountSyscalls())
231		return NULL;
232
233	return (*fSyscalls)[index];
234}
235
236
237Syscall*
238SyscallVector::CreateSyscall(const char* name, const char* kernelName)
239{
240	Syscall* syscall = new Syscall(name, kernelName);
241	fSyscalls->push_back(syscall);
242	return syscall;
243}
244
245
246// #pragma mark -
247
248
249class Main {
250public:
251	int Run(int argc, char** argv)
252	{
253		// parse arguments
254		const char* syscallsFile = NULL;
255		const char* dispatcherFile = NULL;
256		const char* numbersFile = NULL;
257		const char* tableFile = NULL;
258		const char* straceFile = NULL;
259
260		for (int argi = 1; argi < argc; argi++) {
261			string arg(argv[argi]);
262			if (arg == "-h" || arg == "--help") {
263				print_usage(false);
264				return 0;
265			} else if (arg == "-c") {
266				if (argi + 1 >= argc) {
267					print_usage(true);
268					return 1;
269				}
270				syscallsFile = argv[++argi];
271			} else if (arg == "-d") {
272				if (argi + 1 >= argc) {
273					print_usage(true);
274					return 1;
275				}
276				dispatcherFile = argv[++argi];
277			} else if (arg == "-n") {
278				if (argi + 1 >= argc) {
279					print_usage(true);
280					return 1;
281				}
282				numbersFile = argv[++argi];
283			} else if (arg == "-t") {
284				if (argi + 1 >= argc) {
285					print_usage(true);
286					return 1;
287				}
288				tableFile = argv[++argi];
289			} else if (arg == "-s") {
290				if (argi + 1 >= argc) {
291					print_usage(true);
292					return 1;
293				}
294				straceFile = argv[++argi];
295			} else {
296				print_usage(true);
297				return 1;
298			}
299		}
300		fSyscallVector = create_syscall_vector();
301		fSyscallCount = fSyscallVector->CountSyscalls();
302		if (!syscallsFile && !dispatcherFile && !numbersFile && !tableFile
303			&& !straceFile) {
304			printf("Found %d syscalls.\n", fSyscallCount);
305			return 0;
306		}
307		// generate the output
308		if (syscallsFile)
309			_WriteSyscallsFile(syscallsFile);
310		if (dispatcherFile)
311			_WriteDispatcherFile(dispatcherFile);
312		if (numbersFile)
313			_WriteNumbersFile(numbersFile);
314		if (tableFile)
315			_WriteTableFile(tableFile);
316		if (straceFile)
317			_WriteSTraceFile(straceFile);
318		return 0;
319	}
320
321	void _WriteSyscallsFile(const char* filename)
322	{
323		// open the syscalls output file
324		ofstream file(filename, ofstream::out | ofstream::trunc);
325		if (!file.is_open())
326			throw IOException(string("Failed to open `") + filename + "'.");
327
328		// output the syscalls definitions
329		for (int i = 0; i < fSyscallCount; i++) {
330			const Syscall* syscall = fSyscallVector->SyscallAt(i);
331			int paramCount = syscall->CountParameters();
332			int paramSize = 0;
333			// XXX: Currently the SYSCALL macros support 4 byte aligned
334			// parameters only. This has to change, of course.
335			for (int k = 0; k < paramCount; k++) {
336				const Parameter* parameter = syscall->ParameterAt(k);
337				int size = parameter->UsedSize();
338				paramSize += (size + 3) / 4 * 4;
339			}
340			file << "SYSCALL" << (paramSize / 4) << "("
341				<< syscall->Name() << ", " << i << ")" << endl;
342		}
343	}
344
345	void _WriteDispatcherFile(const char* filename)
346	{
347		// open the dispatcher output file
348		ofstream file(filename, ofstream::out | ofstream::trunc);
349		if (!file.is_open())
350			throw IOException(string("Failed to open `") + filename + "'.");
351
352		// output the case statements
353		for (int i = 0; i < fSyscallCount; i++) {
354			const Syscall* syscall = fSyscallVector->SyscallAt(i);
355			file << "case " << i << ":" << endl;
356			file << "\t";
357			if (string(syscall->ReturnType()->TypeName()) != "void")
358				file << "*_returnValue = ";
359			file << syscall->KernelName() << "(";
360			int paramCount = syscall->CountParameters();
361			if (paramCount > 0) {
362				Parameter* parameter = syscall->ParameterAt(0);
363				if (parameter->AlignmentTypeName()) {
364					file << "(" << parameter->TypeName() << ")*("
365						 << parameter->AlignmentTypeName()
366						 << "*)args";
367				} else {
368					file << "*(" << _GetPointerType(parameter->TypeName())
369						<< ")args";
370				}
371				for (int k = 1; k < paramCount; k++) {
372					parameter = syscall->ParameterAt(k);
373					if (parameter->AlignmentTypeName()) {
374						file << ", (" << parameter->TypeName() << ")*("
375							<< parameter->AlignmentTypeName()
376							<< "*)((char*)args + " << parameter->Offset()
377							<< ")";
378					} else {
379						file << ", *(" << _GetPointerType(parameter->TypeName())
380							<< ")((char*)args + " << parameter->Offset()
381							<< ")";
382					}
383				}
384			}
385			file << ");" << endl;
386			file << "\tbreak;" << endl;
387		}
388	}
389
390	void _WriteNumbersFile(const char* filename)
391	{
392		// open the syscall numbers output file
393		ofstream file(filename, ofstream::out | ofstream::trunc);
394		if (!file.is_open())
395			throw IOException(string("Failed to open `") + filename + "'.");
396
397		// output the defines
398		const char* prefix = "_user_";
399		size_t prefixLen = strlen(prefix);
400		for (int i = 0; i < fSyscallCount; i++) {
401			const Syscall* syscall = fSyscallVector->SyscallAt(i);
402			string name(syscall->KernelName());
403
404			// drop the leading "_user_" prefix
405			if (name.find(prefix) != 0)
406				throw Exception(string("Bad kernel name: `") + name + "'.");
407			name = string(name, prefixLen);
408
409			// convert to upper case (is there no function for that?)
410			string defineName;
411			for (int k = 0; k < (int)name.length(); k++)
412				defineName += toupper(name[k]);
413			file << "#define SYSCALL_" << defineName << " " << i << endl;
414		}
415	}
416
417	void _WriteTableFile(const char* filename)
418	{
419		// open the syscall table output file
420		ofstream file(filename, ofstream::out | ofstream::trunc);
421		if (!file.is_open())
422			throw IOException(string("Failed to open `") + filename + "'.");
423
424		// output syscall count macro
425		file << "#define SYSCALL_COUNT " << fSyscallCount << endl;
426		file << endl;
427
428		// assembler guard
429		file << "#ifndef _ASSEMBLER" << endl;
430		file << endl;
431
432		// includes
433		file << "#include <TypeConstants.h>" << endl;
434		file << endl;
435
436		// output syscall count
437		file << "const int kSyscallCount = SYSCALL_COUNT;" << endl;
438		file << endl;
439
440		// syscall infos array preamble
441		file << "const syscall_info kSyscallInfos[] = {" << endl;
442
443		// the syscall infos
444		for (int i = 0; i < fSyscallCount; i++) {
445			const Syscall* syscall = fSyscallVector->SyscallAt(i);
446
447			// get the parameter size
448			int paramSize = 0;
449			if (Parameter* parameter = syscall->LastParameter())
450				paramSize = parameter->Offset() + parameter->UsedSize();
451
452			// output the info for the syscall
453			file << "\t{ (void *)" << syscall->KernelName() << ", "
454				<< paramSize << " }," << endl;
455		}
456
457		// syscall infos array end
458		file << "};" << endl;
459		file << endl;
460
461		// syscall parameters infos array preamble
462		file << "const extended_syscall_info kExtendedSyscallInfos[] = {"
463			<< endl;
464
465		// the syscall parameters infos
466		for (int i = 0; i < fSyscallCount; i++) {
467			const Syscall* syscall = fSyscallVector->SyscallAt(i);
468			int paramCount = syscall->CountParameters();
469
470			file << "\t{" << endl;
471			file << "\t\t\"" << syscall->Name() << "\", " << paramCount << ","
472				<< endl;
473
474			// return type
475			Type* returnType = syscall->ReturnType();
476			file << "\t\t{ " << returnType->Size() << ", "
477				<< returnType->UsedSize() << ", "
478				<< _GetTypeCode(returnType) << " }," << endl;
479
480			// parameters
481			file << "\t\t{" << endl;
482
483			for (int k = 0; k < paramCount; k++) {
484				const Parameter* parameter = syscall->ParameterAt(k);
485				file << "\t\t\t{ " << parameter->Offset() << ", "
486					<< parameter->Size() << ", "
487					<< parameter->UsedSize() << ", "
488					<< _GetTypeCode(parameter) << " }," << endl;
489			}
490
491			file << "\t\t}" << endl;
492			file << "\t}," << endl;
493		}
494
495		// syscall parameters infos array end
496		file << "};" << endl;
497
498		// assembler guard end
499		file << "#endif	// _ASSEMBLER" << endl;
500	}
501
502	void _WriteSTraceFile(const char* filename)
503	{
504		// open the syscall table output file
505		ofstream file(filename, ofstream::out | ofstream::trunc);
506		if (!file.is_open())
507			throw IOException(string("Failed to open `") + filename + "'.");
508
509		// the file defines a single function get_syscalls
510		file << "void" << endl
511			<< "GET_SYSCALLS(vector<Syscall*> &syscalls)" << endl
512			<< "{" << endl
513			<< "\tSyscall *syscall;" << endl
514			<< "\tTypeHandler *handler;" << endl
515			<< "(void)syscall;" << endl
516			<< "(void)handler;" << endl;
517
518		int chunkSize = (fSyscallCount + 19) / 20;
519
520		// iterate through the syscalls
521		for (int i = 0; i < fSyscallCount; i++) {
522			const Syscall* syscall = fSyscallVector->SyscallAt(i);
523
524			if (i % chunkSize == 0) {
525				// chunk end
526				file << endl;
527				if (i > 0)
528					file << "#endif" << endl;
529				// chunk begin
530				file << "#ifdef SYSCALLS_CHUNK_" << (i / chunkSize) << endl;
531			}
532
533			// spacing, comment
534			file << endl;
535			file << "\t// " << syscall->Name() << endl;
536
537			// create the return type handler
538			const char* returnType = syscall->ReturnType()->TypeName();
539			file << "\thandler = TypeHandlerFactory<"
540				<< returnType
541				<< ">::Create();" << endl;
542
543			// create the syscall
544			file << "\tsyscall = new Syscall(\"" << syscall->Name() << "\", "
545				<< "\"" << returnType << "\", "<< "handler);" << endl;
546			file << "\tsyscalls.push_back(syscall);" << endl;
547
548			// add the parameters
549			int paramCount = syscall->CountParameters();
550			for (int k = 0; k < paramCount; k++) {
551				const Parameter* parameter = syscall->ParameterAt(k);
552
553				// create the parameter type handler
554				file << "\thandler = TypeHandlerFactory<"
555					<< parameter->TypeName() << ">::Create();" << endl;
556
557				// add the parameter
558				file << "\tsyscall->AddParameter(\""
559					<< parameter->ParameterName() << "\", "
560					<< parameter->Offset() << ", \"" << parameter->TypeName()
561					<< "\", handler);" << endl;
562			}
563		}
564
565		// last syscall chunk end
566		file << "#endif" << endl;
567
568		// function end
569		file << "}" << endl;
570	}
571
572	static string _GetPointerType(const char* type)
573	{
574		const char* parenthesis = strchr(type, ')');
575		if (!parenthesis)
576			return string(type) + "*";
577		// function pointer type
578		return string(type, parenthesis - type) + "*" + parenthesis;
579	}
580
581	static string _GetTypeCode(const Type* type)
582	{
583		const char* typeName = type->TypeName();
584		if (strchr(typeName, '*')) {
585			// pointer type
586			// check, if it is a string constant ("const char *" or
587			// "char const *")
588			if ((_GetTypeCodeTokenize(typeName) == "const"
589					&& _GetTypeCodeTokenize(typeName) == "char"
590					&& _GetTypeCodeTokenize(typeName) == "*"
591					&& _GetTypeCodeTokenize(typeName) == "")
592				|| (_GetTypeCodeTokenize(typeName) == "char"
593					&& _GetTypeCodeTokenize(typeName) == "const"
594					&& _GetTypeCodeTokenize(typeName) == "*"
595					&& _GetTypeCodeTokenize(typeName) == "")) {
596				return "B_STRING_TYPE";
597			}
598
599			// not a string constant
600			return "B_POINTER_TYPE";
601		} else {
602			// non-pointer type
603			switch (type->Size()) {
604				case 1:
605					return "B_INT8_TYPE";
606				case 2:
607					return "B_INT16_TYPE";
608				case 4:
609					return "B_INT32_TYPE";
610				case 8:
611					return "B_INT64_TYPE";
612				default:
613					return "B_RAW_TYPE";
614			}
615		}
616	}
617
618	static string _GetTypeCodeTokenize(const char*& type)
619	{
620		// skip whitespace
621		while (*type != '\0' && isspace(*type))
622			type++;
623
624		switch (*type) {
625			case '\0':
626				return "";
627
628			case '*':
629			case '(':
630			case ')':
631			case '&':
632				return string(type++, 1);
633
634			default:
635			{
636				if (*type != '_' && !isalpha(*type)) {
637					// probably invalid, just return something
638					return string(type++, 1);
639				}
640
641				// an identifier
642				const char* start = type;
643				while (*type == '_' || isalnum(*type))
644					type++;
645				return string(start, type - start);
646			}
647		}
648	}
649
650private:
651	SyscallVector*	fSyscallVector;
652	int				fSyscallCount;
653};
654
655
656int
657main(int argc, char** argv)
658{
659	try {
660		return Main().Run(argc, argv);
661	} catch (Exception &exception) {
662		fprintf(stderr, "%s\n", exception.what());
663		return 1;
664	}
665}
666