1 // gensyscalls.cpp 2 // 3 // Copyright (c) 2004, Ingo Weinhold <bonefish@cs.tu-berlin.de> 4 // All rights reserved. Distributed under the terms of the OpenBeOS License. 5 6 #include <fcntl.h> 7 #include <fstream> 8 #include <list> 9 #include <stack> 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <string> 13 #include <vector> 14 15 // usage 16 const char *kUsage = 17 "Usage: gensyscalls <header> <calls> <dispatcher>\n" 18 "\n" 19 "Given the (preprocessed) header file that defines the syscall prototypes the\n" 20 "command generates a piece of assembly source file that defines the actual\n" 21 "syscalls and a piece of C source (cases of a switch statement) for the \n" 22 "kernel syscall dispatcher. Either file is to be included from a respective\n" 23 "skeleton source file.\n" 24 "\n" 25 " <header> - Input: The preprocessed header file with the\n" 26 " syscall prototypes.\n" 27 " <calls> - Output: The assembly source file implementing the\n" 28 " actual syscalls." 29 " <dispatcher> - Output: The C source file to be included by the\n" 30 " syscall dispatcher source file.\n"; 31 32 // print_usage 33 static 34 void 35 print_usage(bool error) 36 { 37 fprintf((error ? stderr : stdout), kUsage); 38 } 39 40 // Type 41 struct Type { 42 Type(const char *type) : type(type) {} 43 Type(const string &type) : type(type) {} 44 45 string type; 46 }; 47 48 // Function 49 class Function { 50 public: 51 Function() : fReturnType("") {} 52 53 void SetName(const string &name) 54 { 55 fName = name; 56 } 57 58 const string &GetName() const 59 { 60 return fName; 61 } 62 63 void AddParameter(const Type &type) 64 { 65 fParameters.push_back(type); 66 } 67 68 int CountParameters() const 69 { 70 return fParameters.size(); 71 } 72 73 const Type &ParameterAt(int index) const 74 { 75 return fParameters[index]; 76 } 77 78 void SetReturnType(const Type &type) 79 { 80 fReturnType = type; 81 } 82 83 const Type &GetReturnType() const 84 { 85 return fReturnType; 86 } 87 88 protected: 89 string fName; 90 vector<Type> fParameters; 91 Type fReturnType; 92 }; 93 94 // Syscall 95 class Syscall : public Function { 96 public: 97 string GetKernelName() const 98 { 99 int baseIndex = 0; 100 if (fName.find("_kern_") == 0) 101 baseIndex = strlen("_kern_"); 102 else if (fName.find("sys_") == 0) 103 baseIndex = strlen("sys_"); 104 string kernelName("_user_"); 105 kernelName.append(string(fName, baseIndex)); 106 return kernelName; 107 } 108 }; 109 110 // Exception 111 struct Exception : exception { 112 Exception() 113 : fMessage() 114 { 115 } 116 117 Exception(const string &message) 118 : fMessage(message) 119 { 120 } 121 122 virtual ~Exception() {} 123 124 virtual const char *what() const 125 { 126 return fMessage.c_str(); 127 } 128 129 private: 130 string fMessage; 131 }; 132 133 // EOFException 134 struct EOFException : public Exception { 135 EOFException() {} 136 EOFException(const string &message) : Exception(message) {} 137 virtual ~EOFException() {} 138 }; 139 140 // IOException 141 struct IOException : public Exception { 142 IOException() {} 143 IOException(const string &message) : Exception(message) {} 144 virtual ~IOException() {} 145 }; 146 147 // ParseException 148 struct ParseException : public Exception { 149 ParseException() {} 150 ParseException(const string &message) : Exception(message) {} 151 virtual ~ParseException() {} 152 }; 153 154 155 // Tokenizer 156 class Tokenizer { 157 public: 158 Tokenizer(istream &input) 159 : fInput(input), 160 fHasCurrent(false) 161 { 162 } 163 164 string GetCurrentToken() 165 { 166 if (!fHasCurrent) 167 throw Exception("GetCurrentToken(): No current token!"); 168 return fTokens.front(); 169 } 170 171 string GetNextToken() 172 { 173 return GetNextToken(NULL); 174 } 175 176 string GetNextToken(stack<string> &skippedTokens) 177 { 178 return GetNextToken(&skippedTokens); 179 } 180 181 string GetNextToken(stack<string> *skippedTokens) 182 { 183 if (fHasCurrent) { 184 fTokens.pop_front(); 185 fHasCurrent = false; 186 } 187 while (fTokens.empty()) 188 _ReadLine(); 189 fHasCurrent = true; 190 if (skippedTokens) 191 skippedTokens->push(fTokens.front()); 192 return fTokens.front(); 193 } 194 195 void ExpectToken(const string &expectedToken) 196 { 197 string token = GetCurrentToken(); 198 if (expectedToken != token) { 199 throw ParseException(string("Unexpected token `") + token 200 + "'. Expected was `" + expectedToken + "'."); 201 } 202 } 203 204 void ExpectNextToken(const string &expectedToken) 205 { 206 GetNextToken(); 207 ExpectToken(expectedToken); 208 } 209 210 bool CheckToken(const string &expectedToken) 211 { 212 string token = GetCurrentToken(); 213 return (expectedToken == token); 214 } 215 216 bool CheckNextToken(const string &expectedToken) 217 { 218 GetNextToken(); 219 bool result = CheckToken(expectedToken); 220 if (!result) 221 PutCurrentToken(); 222 return result; 223 } 224 225 void PutCurrentToken() 226 { 227 if (!fHasCurrent) 228 throw Exception("GetCurrentToken(): No current token!"); 229 fHasCurrent = false; 230 } 231 232 void PutToken(string token) 233 { 234 if (fHasCurrent) { 235 fTokens.pop_front(); 236 fHasCurrent = false; 237 } 238 fTokens.push_front(token); 239 } 240 241 void PutTokens(stack<string> &tokens) 242 { 243 if (fHasCurrent) { 244 fTokens.pop_front(); 245 fHasCurrent = false; 246 } 247 while (!tokens.empty()) { 248 fTokens.push_front(tokens.top()); 249 tokens.pop(); 250 } 251 } 252 253 private: 254 void _ReadLine() 255 { 256 // read the line 257 char buffer[10240]; 258 if (!fInput.getline(buffer, sizeof(buffer))) 259 throw EOFException("Unexpected end of input."); 260 // parse it 261 int len = strlen(buffer); 262 int tokenStart = 0; 263 for (int i = 0; i < len; i++) { 264 char c = buffer[i]; 265 if (isspace(c)) { 266 if (tokenStart < i) 267 fTokens.push_back(string(buffer + tokenStart, buffer + i)); 268 tokenStart = i + 1; 269 continue; 270 } 271 switch (buffer[i]) { 272 case '#': 273 case '(': 274 case ')': 275 case '*': 276 case '&': 277 case '[': 278 case ']': 279 case ';': 280 case ',': 281 if (tokenStart < i) { 282 fTokens.push_back(string(buffer + tokenStart, 283 buffer + i)); 284 } 285 fTokens.push_back(string(buffer + i, buffer + i + 1)); 286 tokenStart = i + 1; 287 break; 288 } 289 } 290 if (tokenStart < len) 291 fTokens.push_back(string(buffer + tokenStart, buffer + len)); 292 } 293 294 private: 295 istream &fInput; 296 list<string> fTokens; 297 bool fHasCurrent; 298 }; 299 300 301 // Main 302 class Main { 303 public: 304 305 int Run(int argc, char **argv) 306 { 307 // parse parameters 308 if (argc >= 2 309 && (string(argv[1]) == "-h" || string(argv[1]) == "--help")) { 310 print_usage(false); 311 return 0; 312 } 313 if (argc != 4) { 314 print_usage(true); 315 return 1; 316 } 317 _ParseSyscalls(argv[1]); 318 _WriteSyscallsFile(argv[2]); 319 _WriteDispatcherFile(argv[3]); 320 return 0; 321 } 322 323 324 private: 325 void _ParseSyscalls(const char *filename) 326 { 327 // open the input file 328 ifstream file(filename, ifstream::in); 329 if (!file.is_open()) 330 throw new IOException(string("Failed to open `") + filename + "'."); 331 // parse the syscalls 332 Tokenizer tokenizer(file); 333 // find "#pragma syscalls begin" 334 while (true) { 335 while (tokenizer.GetNextToken() != "#"); 336 stack<string> skippedTokens; 337 if (tokenizer.GetNextToken(skippedTokens) == "pragma" 338 && tokenizer.GetNextToken(skippedTokens) == "syscalls" 339 && tokenizer.GetNextToken(skippedTokens) == "begin") { 340 break; 341 } 342 tokenizer.PutTokens(skippedTokens); 343 } 344 // parse the functions 345 while (!tokenizer.CheckNextToken("#")) { 346 Syscall syscall; 347 _ParseSyscall(tokenizer, syscall); 348 fSyscalls.push_back(syscall); 349 } 350 // expect "pragma syscalls end" 351 tokenizer.ExpectNextToken("pragma"); 352 tokenizer.ExpectNextToken("syscalls"); 353 tokenizer.ExpectNextToken("end"); 354 355 // for (int i = 0; i < (int)fSyscalls.size(); i++) { 356 // const Syscall &syscall = fSyscalls[i]; 357 // printf("syscall: `%s'\n", syscall.GetName().c_str()); 358 // for (int k = 0; k < (int)syscall.CountParameters(); k++) 359 // printf(" arg: `%s'\n", syscall.ParameterAt(k).type.c_str()); 360 // printf(" return type: `%s'\n", 361 // syscall.GetReturnType().type.c_str()); 362 // } 363 // printf("Found %lu syscalls.\n", fSyscalls.size()); 364 } 365 366 void _WriteSyscallsFile(const char *filename) 367 { 368 // open the syscalls output file 369 ofstream file(filename, ofstream::out | ofstream::trunc); 370 if (!file.is_open()) 371 throw new IOException(string("Failed to open `") + filename + "'."); 372 // output the syscalls definitions 373 for (int i = 0; i < (int)fSyscalls.size(); i++) { 374 const Syscall &syscall = fSyscalls[i]; 375 file << "SYSCALL" << syscall.CountParameters() << "(" 376 << syscall.GetName() << ", " << i << ")" << endl; 377 } 378 } 379 380 void _WriteDispatcherFile(const char *filename) 381 { 382 // open the dispatcher output file 383 ofstream file(filename, ofstream::out | ofstream::trunc); 384 if (!file.is_open()) 385 throw new IOException(string("Failed to open `") + filename + "'."); 386 // output the case statements 387 for (int i = 0; i < (int)fSyscalls.size(); i++) { 388 const Syscall &syscall = fSyscalls[i]; 389 file << "case " << i << ":" << endl; 390 file << "\t"; 391 if (syscall.GetReturnType().type != "void") 392 file << "*call_ret = "; 393 file << syscall.GetKernelName() << "("; 394 if (syscall.CountParameters() > 0) { 395 file << "(" << syscall.ParameterAt(0).type << ")arg0"; 396 for (int k = 1; k < (int)syscall.CountParameters(); k++) 397 file << ", (" << syscall.ParameterAt(k).type << ")arg" << k; 398 } 399 file << ");" << endl; 400 file << "\tbreak;" << endl; 401 } 402 } 403 404 void _ParseSyscall(Tokenizer &tokenizer, Syscall &syscall) 405 { 406 // get return type and function name 407 vector<string> returnType; 408 while (tokenizer.GetNextToken() != "(") { 409 string token = tokenizer.GetCurrentToken(); 410 // strip leading "extern" 411 if (!returnType.empty() || token != "extern") 412 returnType.push_back(token); 413 } 414 if (returnType.size() < 2) { 415 throw ParseException("Error while parsing function " 416 "return type."); 417 } 418 syscall.SetName(returnType[returnType.size() - 1]); 419 returnType.pop_back(); 420 string returnTypeString(returnType[0]); 421 for (int i = 1; i < (int)returnType.size(); i++) { 422 returnTypeString += " "; 423 returnTypeString += returnType[i]; 424 } 425 syscall.SetReturnType(returnTypeString); 426 // get arguments 427 if (!tokenizer.CheckNextToken(")")) { 428 _ParseParameter(tokenizer, syscall); 429 while (!tokenizer.CheckNextToken(")")) { 430 tokenizer.ExpectNextToken(","); 431 _ParseParameter(tokenizer, syscall); 432 } 433 } 434 tokenizer.ExpectNextToken(";"); 435 } 436 437 void _ParseParameter(Tokenizer &tokenizer, Syscall &syscall) 438 { 439 vector<string> type; 440 while (tokenizer.GetNextToken() != ")" 441 && tokenizer.GetCurrentToken() != ",") { 442 string token = tokenizer.GetCurrentToken(); 443 type.push_back(token); 444 if (token == "(") { 445 // This must be a function pointer. We deal with that in a 446 // separate method. 447 _ParseFunctionPointerParameter(tokenizer, syscall, type); 448 return; 449 } 450 } 451 tokenizer.PutCurrentToken(); 452 if (type.size() < 2) { 453 if (type.size() == 1 && type[0] == "void") { 454 // that's OK 455 return; 456 } 457 throw ParseException("Error while parsing function " 458 "parameter."); 459 } 460 type.pop_back(); // last component is the parameter name 461 string typeString(type[0]); 462 for (int i = 1; i < (int)type.size(); i++) { 463 typeString += " "; 464 typeString += type[i]; 465 } 466 syscall.AddParameter(typeString); 467 } 468 469 void _ParseFunctionPointerParameter(Tokenizer &tokenizer, Syscall &syscall, 470 vector<string> &type) 471 { 472 // When this method is entered, the return type and the opening 473 // parenthesis must already be parse and stored in the supplied type 474 // vector. 475 if (type.size() < 2) { 476 throw ParseException("Error parsing function parameter. " 477 "No return type."); 478 } 479 // read all the "*"s there are 480 while (tokenizer.CheckNextToken("*")) 481 type.push_back("*"); 482 // now comes the parameter name, if specified -- skip it 483 if (!tokenizer.CheckNextToken(")")) { 484 tokenizer.GetNextToken(); 485 tokenizer.ExpectNextToken(")"); 486 } 487 type.push_back(")"); 488 // the function parameters 489 tokenizer.ExpectNextToken("("); 490 type.push_back("("); 491 while (!tokenizer.CheckNextToken(")")) 492 type.push_back(tokenizer.GetNextToken()); 493 type.push_back(")"); 494 // compose the type string and add it to the syscall parameters 495 string typeString(type[0]); 496 for (int i = 1; i < (int)type.size(); i++) { 497 typeString += " "; 498 typeString += type[i]; 499 } 500 syscall.AddParameter(typeString); 501 } 502 503 private: 504 vector<Syscall> fSyscalls; 505 }; 506 507 508 // main 509 int 510 main(int argc, char **argv) 511 { 512 try { 513 return Main().Run(argc, argv); 514 } catch (Exception &exception) { 515 fprintf(stderr, "%s\n", exception.what()); 516 return 1; 517 } 518 } 519 520