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 // take care of extra alignment for long parameters 170 // this is needed to sort out parameter offsets on ARM 171 if (size >= kLongParameterAlignmentSize) { 172 if ((offset % kLongParameterAlignmentSize) != 0) { 173 offset += kLongParameterAlignmentSize 174 - offset % kLongParameterAlignmentSize; 175 } 176 } 177 178 int usedSize = (size + kParameterAlignmentSize - 1) 179 / kParameterAlignmentSize * kParameterAlignmentSize; 180 const char* alignmentType 181 = size != usedSize && size < kParameterAlignmentSize 182 ? kParameterAlignmentType : 0; 183 184 AddParameter(typeName, parameterName, size, usedSize, offset, 185 alignmentType); 186 } 187 188 189 // #pragma mark - SyscallVector 190 191 192 struct SyscallVector::_SyscallVector : public vector<Syscall*> { 193 }; 194 195 196 197 SyscallVector::SyscallVector() 198 : 199 fSyscalls(new _SyscallVector) 200 { 201 } 202 203 204 SyscallVector::~SyscallVector() 205 { 206 int count = CountSyscalls(); 207 for (int i = 0; i < count; i++) 208 delete SyscallAt(i); 209 delete fSyscalls; 210 } 211 212 213 SyscallVector* 214 SyscallVector::Create() 215 { 216 return new SyscallVector; 217 } 218 219 220 int 221 SyscallVector::CountSyscalls() const 222 { 223 return fSyscalls->size(); 224 } 225 226 227 Syscall* 228 SyscallVector::SyscallAt(int index) const 229 { 230 if (index < 0 || index >= CountSyscalls()) 231 return NULL; 232 233 return (*fSyscalls)[index]; 234 } 235 236 237 Syscall* 238 SyscallVector::CreateSyscall(const char* name, const char* kernelName) 239 { 240 Syscall* syscall = new Syscall(name, kernelName); 241 fSyscalls->push_back(syscall); 242 return syscall; 243 } 244 245 246 // #pragma mark - 247 248 249 class Main { 250 public: 251 int Run(int argc, char** argv) 252 { 253 // parse arguments 254 const char* syscallsFile = NULL; 255 const char* dispatcherFile = NULL; 256 const char* numbersFile = NULL; 257 const char* tableFile = NULL; 258 const char* straceFile = NULL; 259 260 for (int argi = 1; argi < argc; argi++) { 261 string arg(argv[argi]); 262 if (arg == "-h" || arg == "--help") { 263 print_usage(false); 264 return 0; 265 } else if (arg == "-c") { 266 if (argi + 1 >= argc) { 267 print_usage(true); 268 return 1; 269 } 270 syscallsFile = argv[++argi]; 271 } else if (arg == "-d") { 272 if (argi + 1 >= argc) { 273 print_usage(true); 274 return 1; 275 } 276 dispatcherFile = argv[++argi]; 277 } else if (arg == "-n") { 278 if (argi + 1 >= argc) { 279 print_usage(true); 280 return 1; 281 } 282 numbersFile = argv[++argi]; 283 } else if (arg == "-t") { 284 if (argi + 1 >= argc) { 285 print_usage(true); 286 return 1; 287 } 288 tableFile = argv[++argi]; 289 } else if (arg == "-s") { 290 if (argi + 1 >= argc) { 291 print_usage(true); 292 return 1; 293 } 294 straceFile = argv[++argi]; 295 } else { 296 print_usage(true); 297 return 1; 298 } 299 } 300 fSyscallVector = create_syscall_vector(); 301 fSyscallCount = fSyscallVector->CountSyscalls(); 302 if (!syscallsFile && !dispatcherFile && !numbersFile && !tableFile 303 && !straceFile) { 304 printf("Found %d syscalls.\n", fSyscallCount); 305 return 0; 306 } 307 // generate the output 308 if (syscallsFile) 309 _WriteSyscallsFile(syscallsFile); 310 if (dispatcherFile) 311 _WriteDispatcherFile(dispatcherFile); 312 if (numbersFile) 313 _WriteNumbersFile(numbersFile); 314 if (tableFile) 315 _WriteTableFile(tableFile); 316 if (straceFile) 317 _WriteSTraceFile(straceFile); 318 return 0; 319 } 320 321 void _WriteSyscallsFile(const char* filename) 322 { 323 // open the syscalls output file 324 ofstream file(filename, ofstream::out | ofstream::trunc); 325 if (!file.is_open()) 326 throw IOException(string("Failed to open `") + filename + "'."); 327 328 // output the syscalls definitions 329 for (int i = 0; i < fSyscallCount; i++) { 330 const Syscall* syscall = fSyscallVector->SyscallAt(i); 331 int paramCount = syscall->CountParameters(); 332 int paramSize = 0; 333 // XXX: Currently the SYSCALL macros support 4 byte aligned 334 // parameters only. This has to change, of course. 335 for (int k = 0; k < paramCount; k++) { 336 const Parameter* parameter = syscall->ParameterAt(k); 337 int size = parameter->UsedSize(); 338 paramSize += (size + 3) / 4 * 4; 339 } 340 file << "SYSCALL" << (paramSize / 4) << "(" 341 << syscall->Name() << ", " << i << ")" << endl; 342 } 343 } 344 345 void _WriteDispatcherFile(const char* filename) 346 { 347 // open the dispatcher output file 348 ofstream file(filename, ofstream::out | ofstream::trunc); 349 if (!file.is_open()) 350 throw IOException(string("Failed to open `") + filename + "'."); 351 352 // output the case statements 353 for (int i = 0; i < fSyscallCount; i++) { 354 const Syscall* syscall = fSyscallVector->SyscallAt(i); 355 file << "case " << i << ":" << endl; 356 file << "\t"; 357 if (string(syscall->ReturnType()->TypeName()) != "void") 358 file << "*_returnValue = "; 359 file << syscall->KernelName() << "("; 360 int paramCount = syscall->CountParameters(); 361 if (paramCount > 0) { 362 Parameter* parameter = syscall->ParameterAt(0); 363 if (parameter->AlignmentTypeName()) { 364 file << "(" << parameter->TypeName() << ")*(" 365 << parameter->AlignmentTypeName() 366 << "*)args"; 367 } else { 368 file << "*(" << _GetPointerType(parameter->TypeName()) 369 << ")args"; 370 } 371 for (int k = 1; k < paramCount; k++) { 372 parameter = syscall->ParameterAt(k); 373 if (parameter->AlignmentTypeName()) { 374 file << ", (" << parameter->TypeName() << ")*(" 375 << parameter->AlignmentTypeName() 376 << "*)((char*)args + " << parameter->Offset() 377 << ")"; 378 } else { 379 file << ", *(" << _GetPointerType(parameter->TypeName()) 380 << ")((char*)args + " << parameter->Offset() 381 << ")"; 382 } 383 } 384 } 385 file << ");" << endl; 386 file << "\tbreak;" << endl; 387 } 388 } 389 390 void _WriteNumbersFile(const char* filename) 391 { 392 // open the syscall numbers output file 393 ofstream file(filename, ofstream::out | ofstream::trunc); 394 if (!file.is_open()) 395 throw IOException(string("Failed to open `") + filename + "'."); 396 397 // output the defines 398 const char* prefix = "_user_"; 399 size_t prefixLen = strlen(prefix); 400 for (int i = 0; i < fSyscallCount; i++) { 401 const Syscall* syscall = fSyscallVector->SyscallAt(i); 402 string name(syscall->KernelName()); 403 404 // drop the leading "_user_" prefix 405 if (name.find(prefix) != 0) 406 throw Exception(string("Bad kernel name: `") + name + "'."); 407 name = string(name, prefixLen); 408 409 // convert to upper case (is there no function for that?) 410 string defineName; 411 for (int k = 0; k < (int)name.length(); k++) 412 defineName += toupper(name[k]); 413 file << "#define SYSCALL_" << defineName << " " << i << endl; 414 } 415 } 416 417 void _WriteTableFile(const char* filename) 418 { 419 // open the syscall table output file 420 ofstream file(filename, ofstream::out | ofstream::trunc); 421 if (!file.is_open()) 422 throw IOException(string("Failed to open `") + filename + "'."); 423 424 // output syscall count macro 425 file << "#define SYSCALL_COUNT " << fSyscallCount << endl; 426 file << endl; 427 428 // assembler guard 429 file << "#ifndef _ASSEMBLER" << endl; 430 file << endl; 431 432 // includes 433 file << "#include <TypeConstants.h>" << endl; 434 file << endl; 435 436 // output syscall count 437 file << "const int kSyscallCount = SYSCALL_COUNT;" << endl; 438 file << endl; 439 440 // syscall infos array preamble 441 file << "const syscall_info kSyscallInfos[] = {" << endl; 442 443 // the syscall infos 444 for (int i = 0; i < fSyscallCount; i++) { 445 const Syscall* syscall = fSyscallVector->SyscallAt(i); 446 447 // get the parameter size 448 int paramSize = 0; 449 if (Parameter* parameter = syscall->LastParameter()) 450 paramSize = parameter->Offset() + parameter->UsedSize(); 451 452 // output the info for the syscall 453 file << "\t{ (void *)" << syscall->KernelName() << ", " 454 << paramSize << " }," << endl; 455 } 456 457 // syscall infos array end 458 file << "};" << endl; 459 file << endl; 460 461 // syscall parameters infos array preamble 462 file << "const extended_syscall_info kExtendedSyscallInfos[] = {" 463 << endl; 464 465 // the syscall parameters infos 466 for (int i = 0; i < fSyscallCount; i++) { 467 const Syscall* syscall = fSyscallVector->SyscallAt(i); 468 int paramCount = syscall->CountParameters(); 469 470 file << "\t{" << endl; 471 file << "\t\t\"" << syscall->Name() << "\", " << paramCount << "," 472 << endl; 473 474 // return type 475 Type* returnType = syscall->ReturnType(); 476 file << "\t\t{ " << returnType->Size() << ", " 477 << returnType->UsedSize() << ", " 478 << _GetTypeCode(returnType) << " }," << endl; 479 480 // parameters 481 file << "\t\t{" << endl; 482 483 for (int k = 0; k < paramCount; k++) { 484 const Parameter* parameter = syscall->ParameterAt(k); 485 file << "\t\t\t{ " << parameter->Offset() << ", " 486 << parameter->Size() << ", " 487 << parameter->UsedSize() << ", " 488 << _GetTypeCode(parameter) << " }," << endl; 489 } 490 491 file << "\t\t}" << endl; 492 file << "\t}," << endl; 493 } 494 495 // syscall parameters infos array end 496 file << "};" << endl; 497 498 // assembler guard end 499 file << "#endif // _ASSEMBLER" << endl; 500 } 501 502 void _WriteSTraceFile(const char* filename) 503 { 504 // open the syscall table output file 505 ofstream file(filename, ofstream::out | ofstream::trunc); 506 if (!file.is_open()) 507 throw IOException(string("Failed to open `") + filename + "'."); 508 509 // the file defines a single function get_syscalls 510 file << "void" << endl 511 << "GET_SYSCALLS(vector<Syscall*> &syscalls)" << endl 512 << "{" << endl 513 << "\tSyscall *syscall;" << endl 514 << "\tTypeHandler *handler;" << endl 515 << "(void)syscall;" << endl 516 << "(void)handler;" << endl; 517 518 int chunkSize = (fSyscallCount + 19) / 20; 519 520 // iterate through the syscalls 521 for (int i = 0; i < fSyscallCount; i++) { 522 const Syscall* syscall = fSyscallVector->SyscallAt(i); 523 524 if (i % chunkSize == 0) { 525 // chunk end 526 file << endl; 527 if (i > 0) 528 file << "#endif" << endl; 529 // chunk begin 530 file << "#ifdef SYSCALLS_CHUNK_" << (i / chunkSize) << endl; 531 } 532 533 // spacing, comment 534 file << endl; 535 file << "\t// " << syscall->Name() << endl; 536 537 // create the return type handler 538 const char* returnType = syscall->ReturnType()->TypeName(); 539 file << "\thandler = TypeHandlerFactory<" 540 << returnType 541 << ">::Create();" << endl; 542 543 // create the syscall 544 file << "\tsyscall = new Syscall(\"" << syscall->Name() << "\", " 545 << "\"" << returnType << "\", "<< "handler);" << endl; 546 file << "\tsyscalls.push_back(syscall);" << endl; 547 548 // add the parameters 549 int paramCount = syscall->CountParameters(); 550 for (int k = 0; k < paramCount; k++) { 551 const Parameter* parameter = syscall->ParameterAt(k); 552 553 // create the parameter type handler 554 file << "\thandler = TypeHandlerFactory<" 555 << parameter->TypeName() << ">::Create();" << endl; 556 557 // add the parameter 558 file << "\tsyscall->AddParameter(\"" 559 << parameter->ParameterName() << "\", " 560 << parameter->Offset() << ", \"" << parameter->TypeName() 561 << "\", handler);" << endl; 562 } 563 } 564 565 // last syscall chunk end 566 file << "#endif" << endl; 567 568 // function end 569 file << "}" << endl; 570 } 571 572 static string _GetPointerType(const char* type) 573 { 574 const char* parenthesis = strchr(type, ')'); 575 if (!parenthesis) 576 return string(type) + "*"; 577 // function pointer type 578 return string(type, parenthesis - type) + "*" + parenthesis; 579 } 580 581 static string _GetTypeCode(const Type* type) 582 { 583 const char* typeName = type->TypeName(); 584 if (strchr(typeName, '*')) { 585 // pointer type 586 // check, if it is a string constant ("const char *" or 587 // "char const *") 588 if ((_GetTypeCodeTokenize(typeName) == "const" 589 && _GetTypeCodeTokenize(typeName) == "char" 590 && _GetTypeCodeTokenize(typeName) == "*" 591 && _GetTypeCodeTokenize(typeName) == "") 592 || (_GetTypeCodeTokenize(typeName) == "char" 593 && _GetTypeCodeTokenize(typeName) == "const" 594 && _GetTypeCodeTokenize(typeName) == "*" 595 && _GetTypeCodeTokenize(typeName) == "")) { 596 return "B_STRING_TYPE"; 597 } 598 599 // not a string constant 600 return "B_POINTER_TYPE"; 601 } else { 602 // non-pointer type 603 switch (type->Size()) { 604 case 1: 605 return "B_INT8_TYPE"; 606 case 2: 607 return "B_INT16_TYPE"; 608 case 4: 609 return "B_INT32_TYPE"; 610 case 8: 611 return "B_INT64_TYPE"; 612 default: 613 return "B_RAW_TYPE"; 614 } 615 } 616 } 617 618 static string _GetTypeCodeTokenize(const char*& type) 619 { 620 // skip whitespace 621 while (*type != '\0' && isspace(*type)) 622 type++; 623 624 switch (*type) { 625 case '\0': 626 return ""; 627 628 case '*': 629 case '(': 630 case ')': 631 case '&': 632 return string(type++, 1); 633 634 default: 635 { 636 if (*type != '_' && !isalpha(*type)) { 637 // probably invalid, just return something 638 return string(type++, 1); 639 } 640 641 // an identifier 642 const char* start = type; 643 while (*type == '_' || isalnum(*type)) 644 type++; 645 return string(start, type - start); 646 } 647 } 648 } 649 650 private: 651 SyscallVector* fSyscallVector; 652 int fSyscallCount; 653 }; 654 655 656 int 657 main(int argc, char** argv) 658 { 659 try { 660 return Main().Run(argc, argv); 661 } catch (Exception &exception) { 662 fprintf(stderr, "%s\n", exception.what()); 663 return 1; 664 } 665 } 666