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 // output syscall count 424 file << "const int kSyscallCount = SYSCALL_COUNT;" << endl; 425 file << endl; 426 427 // syscall infos array preamble 428 file << "const syscall_info kSyscallInfos[] = {" << endl; 429 430 // the syscall infos 431 for (int i = 0; i < fSyscallCount; i++) { 432 const Syscall* syscall = fSyscallVector->SyscallAt(i); 433 434 // get the parameter size 435 int paramSize = 0; 436 if (Parameter* parameter = syscall->LastParameter()) 437 paramSize = parameter->Offset() + parameter->UsedSize(); 438 439 // output the info for the syscall 440 file << "\t{ (void *)" << syscall->KernelName() << ", " 441 << paramSize << " }," << endl; 442 } 443 444 // syscall infos array end 445 file << "};" << endl; 446 file << endl; 447 448 // syscall parameters infos array preamble 449 file << "const extended_syscall_info kExtendedSyscallInfos[] = {" 450 << endl; 451 452 // the syscall parameters infos 453 for (int i = 0; i < fSyscallCount; i++) { 454 const Syscall* syscall = fSyscallVector->SyscallAt(i); 455 int paramCount = syscall->CountParameters(); 456 457 file << "\t{" << endl; 458 file << "\t\t\"" << syscall->Name() << "\", " << paramCount << "," 459 << endl; 460 461 // return type 462 Type* returnType = syscall->ReturnType(); 463 file << "\t\t{ " << returnType->Size() << ", " 464 << returnType->UsedSize() << ", " 465 << _GetTypeCode(returnType) << " }," << endl; 466 467 // parameters 468 file << "\t\t{" << endl; 469 470 for (int k = 0; k < paramCount; k++) { 471 const Parameter* parameter = syscall->ParameterAt(k); 472 file << "\t\t\t{ " << parameter->Offset() << ", " 473 << parameter->Size() << ", " 474 << parameter->UsedSize() << ", " 475 << _GetTypeCode(parameter) << " }," << endl; 476 } 477 478 file << "\t\t}" << endl; 479 file << "\t}," << endl; 480 } 481 482 // syscall parameters infos array end 483 file << "};" << endl; 484 485 // assembler guard end 486 file << "#endif // _ASSEMBLER" << endl; 487 } 488 489 void _WriteSTraceFile(const char* filename) 490 { 491 // open the syscall table output file 492 ofstream file(filename, ofstream::out | ofstream::trunc); 493 if (!file.is_open()) 494 throw IOException(string("Failed to open `") + filename + "'."); 495 496 // the file defines a single function get_syscalls 497 file << "void" << endl 498 << "GET_SYSCALLS(vector<Syscall*> &syscalls)" << endl 499 << "{" << endl 500 << "\tSyscall *syscall;" << endl 501 << "\tTypeHandler *handler;" << endl 502 << "(void)syscall;" << endl 503 << "(void)handler;" << endl; 504 505 int chunkSize = (fSyscallCount + 19) / 20; 506 507 // iterate through the syscalls 508 for (int i = 0; i < fSyscallCount; i++) { 509 const Syscall* syscall = fSyscallVector->SyscallAt(i); 510 511 if (i % chunkSize == 0) { 512 // chunk end 513 file << endl; 514 if (i > 0) 515 file << "#endif" << endl; 516 // chunk begin 517 file << "#ifdef SYSCALLS_CHUNK_" << (i / chunkSize) << endl; 518 } 519 520 // spacing, comment 521 file << endl; 522 file << "\t// " << syscall->Name() << endl; 523 524 // create the return type handler 525 const char* returnType = syscall->ReturnType()->TypeName(); 526 file << "\thandler = TypeHandlerFactory<" 527 << returnType 528 << ">::Create();" << endl; 529 530 // create the syscall 531 file << "\tsyscall = new Syscall(\"" << syscall->Name() << "\", " 532 << "\"" << returnType << "\", "<< "handler);" << endl; 533 file << "\tsyscalls.push_back(syscall);" << endl; 534 535 // add the parameters 536 int paramCount = syscall->CountParameters(); 537 for (int k = 0; k < paramCount; k++) { 538 const Parameter* parameter = syscall->ParameterAt(k); 539 540 // create the parameter type handler 541 file << "\thandler = TypeHandlerFactory<" 542 << parameter->TypeName() << ">::Create();" << endl; 543 544 // add the parameter 545 file << "\tsyscall->AddParameter(\"" 546 << parameter->ParameterName() << "\", " 547 << parameter->Offset() << ", \"" << parameter->TypeName() 548 << "\", handler);" << endl; 549 } 550 } 551 552 // last syscall chunk end 553 file << "#endif" << endl; 554 555 // function end 556 file << "}" << endl; 557 } 558 559 static string _GetPointerType(const char* type) 560 { 561 const char* parenthesis = strchr(type, ')'); 562 if (!parenthesis) 563 return string(type) + "*"; 564 // function pointer type 565 return string(type, parenthesis - type) + "*" + parenthesis; 566 } 567 568 static string _GetTypeCode(const Type* type) 569 { 570 const char* typeName = type->TypeName(); 571 if (strchr(typeName, '*')) { 572 // pointer type 573 // check, if it is a string constant ("const char *" or 574 // "char const *") 575 if ((_GetTypeCodeTokenize(typeName) == "const" 576 && _GetTypeCodeTokenize(typeName) == "char" 577 && _GetTypeCodeTokenize(typeName) == "*" 578 && _GetTypeCodeTokenize(typeName) == "") 579 || (_GetTypeCodeTokenize(typeName) == "char" 580 && _GetTypeCodeTokenize(typeName) == "const" 581 && _GetTypeCodeTokenize(typeName) == "*" 582 && _GetTypeCodeTokenize(typeName) == "")) { 583 return "B_STRING_TYPE"; 584 } 585 586 // not a string constant 587 return "B_POINTER_TYPE"; 588 } else { 589 // non-pointer type 590 switch (type->Size()) { 591 case 1: 592 return "B_INT8_TYPE"; 593 case 2: 594 return "B_INT16_TYPE"; 595 case 4: 596 return "B_INT32_TYPE"; 597 case 8: 598 return "B_INT64_TYPE"; 599 default: 600 return "B_RAW_TYPE"; 601 } 602 } 603 } 604 605 static string _GetTypeCodeTokenize(const char*& type) 606 { 607 // skip whitespace 608 while (*type != '\0' && isspace(*type)) 609 type++; 610 611 switch (*type) { 612 case '\0': 613 return ""; 614 615 case '*': 616 case '(': 617 case ')': 618 case '&': 619 return string(type++, 1); 620 621 default: 622 { 623 if (*type != '_' && !isalpha(*type)) { 624 // probably invalid, just return something 625 return string(type++, 1); 626 } 627 628 // an identifier 629 const char* start = type; 630 while (*type == '_' || isalnum(*type)) 631 type++; 632 return string(start, type - start); 633 } 634 } 635 } 636 637 private: 638 SyscallVector* fSyscallVector; 639 int fSyscallCount; 640 }; 641 642 643 int 644 main(int argc, char** argv) 645 { 646 try { 647 return Main().Run(argc, argv); 648 } catch (Exception &exception) { 649 fprintf(stderr, "%s\n", exception.what()); 650 return 1; 651 } 652 } 653