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