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> <syscall infos>\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 327 void _WriteSyscallInfoFile(const char *filename) 328 { 329 // open the syscall info file 330 ofstream file(filename, ofstream::out | ofstream::trunc); 331 if (!file.is_open()) 332 throw new IOException(string("Failed to open `") + filename + "'."); 333 334 // write preamble 335 file << "#include \"gensyscalls.h\"" << endl; 336 file << "#include \"syscalls.h.pp\"" << endl; 337 file << endl; 338 339 file << "SyscallVector* create_syscall_vector() {" << endl; 340 file << "\tSyscallVector* syscallVector = SyscallVector::Create();" 341 << endl; 342 file << "\tSyscall* syscall;" << endl; 343 344 // syscalls 345 for (int i = 0; i < (int)fSyscalls.size(); i++) { 346 const Syscall &syscall = fSyscalls[i]; 347 348 // syscall = syscallVector->CreateSyscall("syscallName", 349 // "syscallKernelName"); 350 file << "\tsyscall = syscallVector->CreateSyscall(\"" 351 << syscall.GetName() << "\", \"" 352 << syscall.GetKernelName() << "\");" << endl; 353 354 const Type &returnType = syscall.GetReturnType(); 355 356 // syscall->SetReturnType<ReturnType>("returnType"); 357 file << "\tsyscall->SetReturnType<" << returnType.type 358 << ">(\"" << returnType.type << "\");" << endl; 359 360 // parameters 361 int paramCount = syscall.CountParameters(); 362 for (int k = 0; k < paramCount; k++) { 363 const NamedType ¶m = syscall.ParameterAt(k); 364 // syscall->AddParameter<ParameterType>("parameterTypeName", 365 // "parameterName"); 366 file << "\tsyscall->AddParameter<" 367 << param.type << ">(\"" 368 << param.type << "\", \"" 369 << param.name << "\");" << endl; 370 } 371 file << endl; 372 } 373 374 // postamble 375 file << "\treturn syscallVector;" << endl; 376 file << "}" << endl; 377 } 378 379 void _ParseSyscall(Tokenizer &tokenizer, Syscall &syscall) 380 { 381 // get return type and function name 382 vector<string> returnType; 383 while (tokenizer.GetNextToken() != "(") { 384 string token = tokenizer.GetCurrentToken(); 385 // strip leading "extern" 386 if (!returnType.empty() || token != "extern") 387 returnType.push_back(token); 388 } 389 if (returnType.size() < 2) { 390 throw ParseException("Error while parsing function " 391 "return type."); 392 } 393 syscall.SetName(returnType[returnType.size() - 1]); 394 returnType.pop_back(); 395 string returnTypeString(returnType[0]); 396 for (int i = 1; i < (int)returnType.size(); i++) { 397 returnTypeString += " "; 398 returnTypeString += returnType[i]; 399 } 400 syscall.SetReturnType(returnTypeString); 401 // get arguments 402 if (!tokenizer.CheckNextToken(")")) { 403 _ParseParameter(tokenizer, syscall); 404 while (!tokenizer.CheckNextToken(")")) { 405 tokenizer.ExpectNextToken(","); 406 _ParseParameter(tokenizer, syscall); 407 } 408 } 409 tokenizer.ExpectNextToken(";"); 410 } 411 412 void _ParseParameter(Tokenizer &tokenizer, Syscall &syscall) 413 { 414 vector<string> type; 415 while (tokenizer.GetNextToken() != ")" 416 && tokenizer.GetCurrentToken() != ",") { 417 string token = tokenizer.GetCurrentToken(); 418 type.push_back(token); 419 if (token == "(") { 420 // This must be a function pointer. We deal with that in a 421 // separate method. 422 _ParseFunctionPointerParameter(tokenizer, syscall, type); 423 return; 424 } 425 } 426 tokenizer.PutCurrentToken(); 427 if (type.size() < 2) { 428 if (type.size() == 1 && type[0] == "void") { 429 // that's OK 430 return; 431 } 432 throw ParseException("Error while parsing function parameter."); 433 } 434 435 // last component is the parameter name 436 string typeName = type.back(); 437 type.pop_back(); 438 439 string typeString(type[0]); 440 for (int i = 1; i < (int)type.size(); i++) { 441 typeString += " "; 442 typeString += type[i]; 443 } 444 syscall.AddParameter(NamedType(typeString, typeName)); 445 } 446 447 void _ParseFunctionPointerParameter(Tokenizer &tokenizer, Syscall &syscall, 448 vector<string> &type) 449 { 450 // When this method is entered, the return type and the opening 451 // parenthesis must already be parse and stored in the supplied type 452 // vector. 453 if (type.size() < 2) { 454 throw ParseException("Error parsing function parameter. " 455 "No return type."); 456 } 457 // read all the "*"s there are 458 while (tokenizer.CheckNextToken("*")) 459 type.push_back("*"); 460 // now comes the parameter name, if specified -- skip it 461 string typeName; 462 if (!tokenizer.CheckNextToken(")")) { 463 typeName = tokenizer.GetNextToken(); 464 tokenizer.ExpectNextToken(")"); 465 } else { 466 throw ParseException("Error while parsing function parameter. " 467 "No parameter name."); 468 } 469 type.push_back(")"); 470 // the function parameters 471 tokenizer.ExpectNextToken("("); 472 type.push_back("("); 473 while (!tokenizer.CheckNextToken(")")) 474 type.push_back(tokenizer.GetNextToken()); 475 type.push_back(")"); 476 // compose the type string and add it to the syscall parameters 477 string typeString(type[0]); 478 for (int i = 1; i < (int)type.size(); i++) { 479 typeString += " "; 480 typeString += type[i]; 481 } 482 syscall.AddParameter(NamedType(typeString, typeName)); 483 } 484 485 private: 486 vector<Syscall> fSyscalls; 487 }; 488 489 490 // main 491 int 492 main(int argc, char **argv) 493 { 494 try { 495 return Main().Run(argc, argv); 496 } catch (Exception &exception) { 497 fprintf(stderr, "%s\n", exception.what()); 498 return 1; 499 } 500 } 501 502