xref: /haiku/src/tools/gensyscalls/gensyscalls.cpp (revision 50ff75180531d097ed514c604eb216a438dc54e7)
1 // gensyscalls.cpp
2 
3 #include <cstdio>
4 #include <cstdlib>
5 #include <fstream>
6 #include <string>
7 
8 #include "arch_config.h"
9 
10 #include "gensyscalls.h"
11 #include "gensyscalls_common.h"
12 
13 extern "C" gensyscall_syscall_info *gensyscall_get_infos(int *count);
14 
15 // usage
16 const char *kUsage =
17 "Usage: gensyscalls [ -c <calls> ] [ -d <dispatcher> ] [ -n <numbers> ]\n"
18 "                   [ -t <table> ] [ -s <strace> ]\n"
19 "\n"
20 "The command is able to generate several syscalls related source files.\n"
21 "\n"
22 "  <calls>                - Output: The assembly source file implementing the\n"
23 "                           actual syscalls.\n"
24 "  <dispatcher>           - Output: The C source file to be included by the\n"
25 "                           syscall dispatcher source file.\n"
26 "  <numbers>              - Output: The C/assembly include files defining the\n"
27 "                           syscall numbers.\n"
28 "  <table>                - Output: A C source file containing an array with\n"
29 "                           infos about the syscalls\n"
30 "  <strace>               - Output: A C source file for strace support.\n"
31 ;
32 
33 // print_usage
34 static
35 void
36 print_usage(bool error)
37 {
38 	fprintf((error ? stderr : stdout), kUsage);
39 }
40 
41 enum {
42 	PARAMETER_ALIGNMENT	= sizeof(FUNCTION_CALL_PARAMETER_ALIGNMENT_TYPE)
43 };
44 
45 // Main
46 class Main {
47 public:
48 
49 	int Run(int argc, char **argv)
50 	{
51 		// parse arguments
52 		const char *syscallsFile = NULL;
53 		const char *dispatcherFile = NULL;
54 		const char *numbersFile = NULL;
55 		const char *tableFile = NULL;
56 		const char *straceFile = NULL;
57 		for (int argi = 1; argi < argc; argi++) {
58 			string arg(argv[argi]);
59 			if (arg == "-h" || arg == "--help") {
60 				print_usage(false);
61 				return 0;
62 			} else if (arg == "-c") {
63 				if (argi + 1 >= argc) {
64 					print_usage(true);
65 					return 1;
66 				}
67 				syscallsFile = argv[++argi];
68 			} else if (arg == "-d") {
69 				if (argi + 1 >= argc) {
70 					print_usage(true);
71 					return 1;
72 				}
73 				dispatcherFile = argv[++argi];
74 			} else if (arg == "-n") {
75 				if (argi + 1 >= argc) {
76 					print_usage(true);
77 					return 1;
78 				}
79 				numbersFile = argv[++argi];
80 			} else if (arg == "-t") {
81 				if (argi + 1 >= argc) {
82 					print_usage(true);
83 					return 1;
84 				}
85 				tableFile = argv[++argi];
86 			} else if (arg == "-s") {
87 				if (argi + 1 >= argc) {
88 					print_usage(true);
89 					return 1;
90 				}
91 				straceFile = argv[++argi];
92 			} else {
93 				print_usage(true);
94 				return 1;
95 			}
96 		}
97 		fSyscallInfos = gensyscall_get_infos(&fSyscallCount);
98 		_UpdateSyscallInfos();
99 		if (!syscallsFile && !dispatcherFile && !numbersFile && !tableFile
100 			&& !straceFile) {
101 			printf("Found %d syscalls.\n", fSyscallCount);
102 			return 0;
103 		}
104 		// generate the output
105 		if (syscallsFile)
106 			_WriteSyscallsFile(syscallsFile);
107 		if (dispatcherFile)
108 			_WriteDispatcherFile(dispatcherFile);
109 		if (numbersFile)
110 			_WriteNumbersFile(numbersFile);
111 		if (tableFile)
112 			_WriteTableFile(tableFile);
113 		if (straceFile)
114 			_WriteSTraceFile(straceFile);
115 		return 0;
116 	}
117 
118 	void _WriteSyscallsFile(const char *filename)
119 	{
120 		// open the syscalls output file
121 		ofstream file(filename, ofstream::out | ofstream::trunc);
122 		if (!file.is_open())
123 			throw IOException(string("Failed to open `") + filename + "'.");
124 		// output the syscalls definitions
125 		for (int i = 0; i < fSyscallCount; i++) {
126 			const gensyscall_syscall_info &syscall = fSyscallInfos[i];
127 			int paramCount = syscall.parameter_count;
128 			int paramSize = 0;
129 			gensyscall_parameter_info* parameters = syscall.parameters;
130 			// XXX: Currently the SYSCALL macros support 4 byte aligned
131 			// parameters only. This has to change, of course.
132 			for (int k = 0; k < paramCount; k++) {
133 				int size = parameters[k].actual_size;
134 				paramSize += (size + 3) / 4 * 4;
135 			}
136 			file << "SYSCALL" << (paramSize / 4) << "("
137 				<< syscall.name << ", " << i << ")" << endl;
138 		}
139 	}
140 
141 	void _WriteDispatcherFile(const char *filename)
142 	{
143 		// open the dispatcher output file
144 		ofstream file(filename, ofstream::out | ofstream::trunc);
145 		if (!file.is_open())
146 			throw IOException(string("Failed to open `") + filename + "'.");
147 		// output the case statements
148 		for (int i = 0; i < fSyscallCount; i++) {
149 			const gensyscall_syscall_info &syscall = fSyscallInfos[i];
150 			file << "case " << i << ":" << endl;
151 			file << "\t";
152 			if (string(syscall.return_type) != "void")
153 				file << "*call_ret = ";
154 			file << syscall.kernel_name << "(";
155 			int paramCount = syscall.parameter_count;
156 			if (paramCount > 0) {
157 				gensyscall_parameter_info* parameters = syscall.parameters;
158 				if (parameters[0].size < PARAMETER_ALIGNMENT) {
159 					file << "(" << parameters[0].type << ")*("
160 						 << "FUNCTION_CALL_PARAMETER_ALIGNMENT_TYPE"
161 						 << "*)args";
162 				} else {
163 					file << "*(" << _GetPointerType(parameters[0].type)
164 						<< ")args";
165 				}
166 				for (int k = 1; k < paramCount; k++) {
167 					if (parameters[k].size < PARAMETER_ALIGNMENT) {
168 						file << ", (" << parameters[k].type << ")*("
169 							<< "FUNCTION_CALL_PARAMETER_ALIGNMENT_TYPE"
170 							<< "*)((char*)args + " << parameters[k].offset
171 							<< ")";
172 					} else {
173 						file << ", *(" << _GetPointerType(parameters[k].type)
174 							<< ")((char*)args + " << parameters[k].offset
175 							<< ")";
176 					}
177 				}
178 			}
179 			file << ");" << endl;
180 			file << "\tbreak;" << endl;
181 		}
182 	}
183 
184 	void _WriteNumbersFile(const char *filename)
185 	{
186 		// open the syscall numbers output file
187 		ofstream file(filename, ofstream::out | ofstream::trunc);
188 		if (!file.is_open())
189 			throw IOException(string("Failed to open `") + filename + "'.");
190 		// output the defines
191 		const char *prefix = "_user_";
192 		size_t prefixLen = strlen(prefix);
193 		for (int i = 0; i < fSyscallCount; i++) {
194 			const gensyscall_syscall_info &syscall = fSyscallInfos[i];
195 			string name(syscall.kernel_name);
196 			// drop the leading "_user_" prefix
197 			if (name.find(prefix) != 0)
198 				throw Exception(string("Bad kernel name: `") + name + "'.");
199 			name = string(name, prefixLen);
200 			// convert to upper case (is there no function for that?)
201 			string defineName;
202 			for (int k = 0; k < (int)name.length(); k++)
203 				defineName += toupper(name[k]);
204 			file << "#define SYSCALL_" << defineName << " " << i << endl;
205 		}
206 	}
207 
208 	void _WriteTableFile(const char *filename)
209 	{
210 		// open the syscall table output file
211 		ofstream file(filename, ofstream::out | ofstream::trunc);
212 		if (!file.is_open())
213 			throw IOException(string("Failed to open `") + filename + "'.");
214 
215 		// output syscall count
216 		file << "const int kSyscallCount = " << fSyscallCount << ";" << endl;
217 		file << endl;
218 
219 		// syscall infos array preamble
220 		file << "const syscall_info kSyscallInfos[] = {" << endl;
221 
222 		// the syscall infos
223 		for (int i = 0; i < fSyscallCount; i++) {
224 			const gensyscall_syscall_info &syscall = fSyscallInfos[i];
225 
226 			// get the parameter size
227 			int paramSize = 0;
228 			if (syscall.parameter_count > 0) {
229 				const gensyscall_parameter_info &lastParam
230 					= syscall.parameters[syscall.parameter_count - 1];
231 				paramSize = lastParam.offset + lastParam.actual_size;
232 			}
233 
234 			// output the info for the syscall
235 			file << "\t{ " << syscall.kernel_name << ", "
236 				<< paramSize << " }," << endl;
237 		}
238 
239 		// syscall infos array end
240 		file << "};" << endl;
241 	}
242 
243 	void _WriteSTraceFile(const char *filename)
244 	{
245 		// open the syscall table output file
246 		ofstream file(filename, ofstream::out | ofstream::trunc);
247 		if (!file.is_open())
248 			throw IOException(string("Failed to open `") + filename + "'.");
249 
250 		// the file defines a single function get_syscalls
251 		file << "void" << endl
252 			<< "GET_SYSCALLS(vector<Syscall*> &syscalls)" << endl
253 			<< "{" << endl
254 			<< "\tSyscall *syscall;" << endl
255 			<< "\tTypeHandler *handler;" << endl
256 			<< "(void)syscall;" << endl
257 			<< "(void)handler;" << endl;
258 
259 		int chunkSize = (fSyscallCount + 19) / 20;
260 
261 		// iterate through the syscalls
262 		for (int i = 0; i < fSyscallCount; i++) {
263 			const gensyscall_syscall_info &syscall = fSyscallInfos[i];
264 
265 			if (i % chunkSize == 0) {
266 				// chunk end
267 				file << endl;
268 				if (i > 0)
269 					file << "#endif" << endl;
270 				// chunk begin
271 				file << "#ifdef SYSCALLS_CHUNK_" << (i / chunkSize) << endl;
272 			}
273 
274 			// spacing, comment
275 			file << endl;
276 			file << "\t// " << syscall.name << endl;
277 
278 			// create the return type handler
279 			file << "\thandler = TypeHandlerFactory<" << syscall.return_type
280 				<< ">::Create();" << endl;
281 
282 			// create the syscall
283 			file << "\tsyscall = new Syscall(\"" << syscall.name << "\", "
284 				<< "\"" << syscall.return_type << "\", "<< "handler);" << endl;
285 			file << "\tsyscalls.push_back(syscall);" << endl;
286 
287 			// add the parameters
288 			for (int k = 0; k < syscall.parameter_count; k++) {
289 				const gensyscall_parameter_info &parameter
290 					= syscall.parameters[k];
291 
292 				// create the parameter type handler
293 				file << "\thandler = TypeHandlerFactory<"
294 					<< parameter.type << ">::Create();" << endl;
295 
296 				// add the parameter
297 				file << "\tsyscall->AddParameter(\"" << parameter.name << "\", "
298 					<< parameter.offset << ", \"" << parameter.type
299 					<< "\", handler);" << endl;
300 			}
301 		}
302 
303 		// last syscall chunk end
304 		file << "#endif" << endl;
305 
306 		// function end
307 		file << "}" << endl;
308 	}
309 
310 	static string _GetPointerType(const char *type)
311 	{
312 		char *parenthesis = strchr(type, ')');
313 		if (!parenthesis)
314 			return string(type) + "*";
315 		// function pointer type
316 		return string(type, parenthesis - type) + "*" + parenthesis;
317 	}
318 
319 	void _UpdateSyscallInfos()
320 	{
321 		// Since getting the parameter offsets and actual sizes doesn't work
322 		// as it is now, we overwrite them with values computed using the
323 		// parameter alignment type.
324 		for (int i = 0; i < fSyscallCount; i++) {
325 			gensyscall_syscall_info &syscall = fSyscallInfos[i];
326 			int paramCount = syscall.parameter_count;
327 			gensyscall_parameter_info* parameters = syscall.parameters;
328 			int offset = 0;
329 			for (int k = 0; k < paramCount; k++) {
330 				if (parameters[k].size < PARAMETER_ALIGNMENT)
331 					parameters[k].actual_size = PARAMETER_ALIGNMENT;
332 				else
333 					parameters[k].actual_size = parameters[k].size;
334 				parameters[k].offset = offset;
335 				offset += parameters[k].actual_size;
336 			}
337 		}
338 	}
339 
340 private:
341 	gensyscall_syscall_info	*fSyscallInfos;
342 	int						fSyscallCount;
343 };
344 
345 // main
346 int
347 main(int argc, char **argv)
348 {
349 	try {
350 		return Main().Run(argc, argv);
351 	} catch (Exception &exception) {
352 		fprintf(stderr, "%s\n", exception.what());
353 		return 1;
354 	}
355 }
356