/* * Copyright 2004-2009, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ #include #include #include #include #include #include #include #include #include "gensyscalls_common.h" #include "arch_gensyscalls.h" // for the alignment type macros (only for the type names) // macro trickery to create a string literal #define MAKE_STRING(x) #x #define EVAL_MACRO(macro, x) macro(x) const char* kUsage = "Usage: gensyscallinfos
\n" "\n" "Given the (preprocessed) header file that defines the syscall prototypes " "the\n" "command generates a source file consisting of syscall infos, which is " "needed\n" "to build gensyscalls, which in turn generates the assembly syscall\n" "definitions and code for the kernelland syscall dispatcher.\n" "\n" "
- Input: The preprocessed header file with the\n" " syscall prototypes.\n" " - Output: The syscall infos source file needed " "to\n" " build gensyscalls.\n" " - Output: A source file that will by another " "build\n" " step turned into a header file included by\n" " .\n"; static void print_usage(bool error) { fprintf((error ? stderr : stdout), kUsage); } struct Type { Type(const char* type) : type(type) {} Type(const string& type) : type(type) {} string type; }; struct NamedType : Type { NamedType(const char* type, const char* name) : Type(type), name(name) {} NamedType(const string& type, const string& name) : Type(type), name(name) {} string name; }; class Function { public: Function() : fReturnType("") {} void SetName(const string& name) { fName = name; } const string& GetName() const { return fName; } void AddParameter(const NamedType& type) { fParameters.push_back(type); } int CountParameters() const { return fParameters.size(); } const NamedType& ParameterAt(int index) const { return fParameters[index]; } void SetReturnType(const Type& type) { fReturnType = type; } const Type& GetReturnType() const { return fReturnType; } protected: string fName; vector fParameters; Type fReturnType; }; class Syscall : public Function { public: string GetKernelName() const { int baseIndex = 0; if (fName.find("_kern_") == 0) baseIndex = strlen("_kern_"); else if (fName.find("sys_") == 0) baseIndex = strlen("sys_"); string kernelName("_user_"); kernelName.append(string(fName, baseIndex)); return kernelName; } }; class Tokenizer { public: Tokenizer(istream& input) : fInput(input), fHasCurrent(false) { } string GetCurrentToken() { if (!fHasCurrent) throw Exception("GetCurrentToken(): No current token!"); return fTokens.front(); } string GetNextToken() { return GetNextToken(NULL); } string GetNextToken(stack& skippedTokens) { return GetNextToken(&skippedTokens); } string GetNextToken(stack* skippedTokens) { if (fHasCurrent) { fTokens.pop_front(); fHasCurrent = false; } while (fTokens.empty()) _ReadLine(); fHasCurrent = true; if (skippedTokens) skippedTokens->push(fTokens.front()); return fTokens.front(); } void ExpectToken(const string& expectedToken) { string token = GetCurrentToken(); if (expectedToken != token) { throw ParseException(string("Unexpected token `") + token + "'. Expected was `" + expectedToken + "'."); } } void ExpectNextToken(const string& expectedToken) { GetNextToken(); ExpectToken(expectedToken); } bool CheckToken(const string& expectedToken) { string token = GetCurrentToken(); return (expectedToken == token); } bool CheckNextToken(const string& expectedToken) { GetNextToken(); bool result = CheckToken(expectedToken); if (!result) PutCurrentToken(); return result; } void PutCurrentToken() { if (!fHasCurrent) throw Exception("GetCurrentToken(): No current token!"); fHasCurrent = false; } void PutToken(string token) { if (fHasCurrent) { fTokens.pop_front(); fHasCurrent = false; } fTokens.push_front(token); } void PutTokens(stack& tokens) { if (fHasCurrent) { fTokens.pop_front(); fHasCurrent = false; } while (!tokens.empty()) { fTokens.push_front(tokens.top()); tokens.pop(); } } private: void _ReadLine() { // read the line char buffer[10240]; if (!fInput.getline(buffer, sizeof(buffer))) throw EOFException("Unexpected end of input."); // parse it vector line; int len = strlen(buffer); int tokenStart = 0; for (int i = 0; i < len; i++) { char c = buffer[i]; if (isspace(c)) { if (tokenStart < i) line.push_back(string(buffer + tokenStart, buffer + i)); tokenStart = i + 1; continue; } switch (buffer[i]) { case '#': case '(': case ')': case '*': case '&': case '[': case ']': case ';': case ',': if (tokenStart < i) { line.push_back(string(buffer + tokenStart, buffer + i)); } line.push_back(string(buffer + i, buffer + i + 1)); tokenStart = i + 1; break; } } if (tokenStart < len) line.push_back(string(buffer + tokenStart, buffer + len)); // drop the line, if it starts with "# ", as those are // directions from the pre-processor to the compiler if (line.size() >= 2) { if (line[0] == "#" && isdigit(line[1][0])) return; } for (int i = 0; i < (int)line.size(); i++) fTokens.push_back(line[i]); } private: istream& fInput; list fTokens; bool fHasCurrent; }; class Main { public: int Run(int argc, char** argv) { // parse parameters if (argc >= 2 && (string(argv[1]) == "-h" || string(argv[1]) == "--help")) { print_usage(false); return 0; } if (argc != 4) { print_usage(true); return 1; } _ParseSyscalls(argv[1]); _WriteSyscallInfoFile(argv[2]); _WriteSyscallTypeSizes(argv[3]); return 0; } private: void _ParseSyscalls(const char* filename) { // open the input file ifstream file(filename, ifstream::in); if (!file.is_open()) throw IOException(string("Failed to open `") + filename + "'."); // parse the syscalls Tokenizer tokenizer(file); // find "#pragma syscalls begin" while (true) { while (tokenizer.GetNextToken() != "#"); stack skippedTokens; if (tokenizer.GetNextToken(skippedTokens) == "pragma" && tokenizer.GetNextToken(skippedTokens) == "syscalls" && tokenizer.GetNextToken(skippedTokens) == "begin") { break; } tokenizer.PutTokens(skippedTokens); } // parse the functions while (!tokenizer.CheckNextToken("#")) { Syscall syscall; _ParseSyscall(tokenizer, syscall); fSyscalls.push_back(syscall); } // expect "pragma syscalls end" tokenizer.ExpectNextToken("pragma"); tokenizer.ExpectNextToken("syscalls"); tokenizer.ExpectNextToken("end"); } void _WriteSyscallInfoFile(const char* filename) { // open the syscall info file ofstream file(filename, ofstream::out | ofstream::trunc); if (!file.is_open()) throw IOException(string("Failed to open `") + filename + "'."); // write preamble file << "#include \"gensyscalls.h\"" << endl; file << "#include \"syscall_types_sizes.h\"" << endl; file << endl; file << "const char* const kReturnTypeAlignmentType = \"" EVAL_MACRO(MAKE_STRING, SYSCALL_RETURN_TYPE_ALIGNMENT_TYPE) << "\";" << endl; file << "const char* const kParameterAlignmentType = \"" EVAL_MACRO(MAKE_STRING, SYSCALL_PARAMETER_ALIGNMENT_TYPE) << "\";" << endl; file << "const int kReturnTypeAlignmentSize = " "SYSCALL_RETURN_TYPE_ALIGNMENT_SIZE;" << endl; file << "const int kParameterAlignmentSize = " "SYSCALL_PARAMETER_ALIGNMENT_SIZE;" << endl; file << endl; file << "SyscallVector* create_syscall_vector() {" << endl; file << "\tSyscallVector* syscallVector = SyscallVector::Create();" << endl; file << "\tSyscall* syscall;" << endl; // syscalls for (int i = 0; i < (int)fSyscalls.size(); i++) { const Syscall& syscall = fSyscalls[i]; // syscall = syscallVector->CreateSyscall("syscallName", // "syscallKernelName"); file << "\tsyscall = syscallVector->CreateSyscall(\"" << syscall.GetName() << "\", \"" << syscall.GetKernelName() << "\");" << endl; const Type& returnType = syscall.GetReturnType(); // syscall->SetReturnType<(SYSCALL_RETURN_TYPE_SIZE_, // "returnType"); file << "\tsyscall->SetReturnType(" << "SYSCALL_RETURN_TYPE_SIZE_" << i << ", \"" << returnType.type << "\");" << endl; // parameters int paramCount = syscall.CountParameters(); for (int k = 0; k < paramCount; k++) { const NamedType& param = syscall.ParameterAt(k); // syscall->AddParameter(SYSCALL_PARAMETER_SIZE__, // "parameterTypeName", "parameterName"); file << "\tsyscall->AddParameter(" << "SYSCALL_PARAMETER_SIZE_" << i << "_" << k << ", \"" << param.type << "\", \"" << param.name << "\");" << endl; } file << endl; } // postamble file << "\treturn syscallVector;" << endl; file << "}" << endl; } void _WriteSyscallTypeSizes(const char* filename) { // open the syscall info file ofstream file(filename, ofstream::out | ofstream::trunc); if (!file.is_open()) throw IOException(string("Failed to open `") + filename + "'."); // write preamble file << "#include " << endl; file << "#include " << endl; file << endl; file << "#include \"arch_gensyscalls.h\"" << endl; file << endl; file << "void dummy() {" << endl; file << "DEFINE_COMPUTED_ASM_MACRO(SYSCALL_RETURN_TYPE_ALIGNMENT_SIZE, " "sizeof(SYSCALL_RETURN_TYPE_ALIGNMENT_TYPE));" << endl; file << "DEFINE_COMPUTED_ASM_MACRO(SYSCALL_PARAMETER_ALIGNMENT_SIZE, " "sizeof(SYSCALL_PARAMETER_ALIGNMENT_TYPE));" << endl; file << endl; // syscalls for (int i = 0; i < (int)fSyscalls.size(); i++) { const Syscall& syscall = fSyscalls[i]; const Type& returnType = syscall.GetReturnType(); if (returnType.type == "void") { file << "DEFINE_COMPUTED_ASM_MACRO(SYSCALL_RETURN_TYPE_SIZE_" << i << ", 0);" << endl; } else { file << "DEFINE_COMPUTED_ASM_MACRO(SYSCALL_RETURN_TYPE_SIZE_" << i << ", sizeof(" << returnType.type << "));" << endl; } // parameters int paramCount = syscall.CountParameters(); for (int k = 0; k < paramCount; k++) { const NamedType& param = syscall.ParameterAt(k); file << "DEFINE_COMPUTED_ASM_MACRO(SYSCALL_PARAMETER_SIZE_" << i << "_" << k << ", sizeof(" << param.type << "));" << endl; } file << endl; } // postamble file << "}" << endl; } void _ParseSyscall(Tokenizer& tokenizer, Syscall& syscall) { // get return type and function name vector returnType; while (tokenizer.GetNextToken() != "(") { string token = tokenizer.GetCurrentToken(); // strip leading "extern" if (!returnType.empty() || token != "extern") returnType.push_back(token); } if (returnType.size() < 2) { throw ParseException("Error while parsing function " "return type."); } syscall.SetName(returnType[returnType.size() - 1]); returnType.pop_back(); string returnTypeString(returnType[0]); for (int i = 1; i < (int)returnType.size(); i++) { returnTypeString += " "; returnTypeString += returnType[i]; } syscall.SetReturnType(returnTypeString); // get arguments if (!tokenizer.CheckNextToken(")")) { _ParseParameter(tokenizer, syscall); while (!tokenizer.CheckNextToken(")")) { tokenizer.ExpectNextToken(","); _ParseParameter(tokenizer, syscall); } } tokenizer.ExpectNextToken(";"); } void _ParseParameter(Tokenizer& tokenizer, Syscall& syscall) { vector type; while (tokenizer.GetNextToken() != ")" && tokenizer.GetCurrentToken() != ",") { string token = tokenizer.GetCurrentToken(); type.push_back(token); if (token == "(") { // This must be a function pointer. We deal with that in a // separate method. _ParseFunctionPointerParameter(tokenizer, syscall, type); return; } } tokenizer.PutCurrentToken(); if (type.size() < 2) { if (type.size() == 1 && type[0] == "void") { // that's OK return; } throw ParseException("Error while parsing function parameter."); } // last component is the parameter name string typeName = type.back(); type.pop_back(); string typeString(type[0]); for (int i = 1; i < (int)type.size(); i++) { typeString += " "; typeString += type[i]; } syscall.AddParameter(NamedType(typeString, typeName)); } void _ParseFunctionPointerParameter(Tokenizer& tokenizer, Syscall& syscall, vector& type) { // When this method is entered, the return type and the opening // parenthesis must already be parse and stored in the supplied type // vector. if (type.size() < 2) { throw ParseException("Error parsing function parameter. " "No return type."); } // read all the "*"s there are while (tokenizer.CheckNextToken("*")) type.push_back("*"); // now comes the parameter name, if specified -- skip it string typeName; if (!tokenizer.CheckNextToken(")")) { typeName = tokenizer.GetNextToken(); tokenizer.ExpectNextToken(")"); } else { throw ParseException("Error while parsing function parameter. " "No parameter name."); } type.push_back(")"); // the function parameters tokenizer.ExpectNextToken("("); type.push_back("("); while (!tokenizer.CheckNextToken(")")) type.push_back(tokenizer.GetNextToken()); type.push_back(")"); // compose the type string and add it to the syscall parameters string typeString(type[0]); for (int i = 1; i < (int)type.size(); i++) { typeString += " "; typeString += type[i]; } syscall.AddParameter(NamedType(typeString, typeName)); } private: vector fSyscalls; }; int main(int argc, char** argv) { try { return Main().Run(argc, argv); } catch (const Exception& exception) { fprintf(stderr, "%s\n", exception.what()); return 1; } }