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