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