/*
* Copyright 2004-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <list>
#include <stack>
#include <string>
#include <string.h>
#include <vector>
#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 <header> <syscall infos> <syscall types sizes>\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"
" <header> - Input: The preprocessed header file with the\n"
" syscall prototypes.\n"
" <syscall infos> - Output: The syscall infos source file needed "
"to\n"
" build gensyscalls.\n"
" <syscall types sizes> - Output: A source file that will by another "
"build\n"
" step turned into a header file included by\n"
" <syscall infos>.\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<NamedType> 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<string>& skippedTokens)
{
return GetNextToken(&skippedTokens);
}
string GetNextToken(stack<string>* 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<string>& 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<string> 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 "# <number>", 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<string> 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 new IOException(string("Failed to open `") + filename + "'.");
// parse the syscalls
Tokenizer tokenizer(file);
// find "#pragma syscalls begin"
while (true) {
while (tokenizer.GetNextToken() != "#");
stack<string> 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 new 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_<i>,
// "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_<i>_<k>,
// "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 new IOException(string("Failed to open `") + filename + "'.");
// write preamble
file << "#include <computed_asm_macros.h>" << endl;
file << "#include <syscalls.h>" << 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<string> 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<string> 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<string>& 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<Syscall> fSyscalls;
};
int
main(int argc, char** argv)
{
try {
return Main().Run(argc, argv);
} catch (Exception& exception) {
fprintf(stderr, "%s\n", exception.what());
return 1;
}
}
↑ V1022 An exception was thrown by pointer. Consider throwing it by value instead.
↑ V1022 An exception was thrown by pointer. Consider throwing it by value instead.
↑ V1022 An exception was thrown by pointer. Consider throwing it by value instead.