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