#include "gensyscalls.h"
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <string>
#include <string.h>
#include <vector>
#include "gensyscalls_common.h"
using std::vector;
static void
print_usage(bool error)
{
fprintf(error ? stderr : stdout,
"Usage: gensyscalls [ -c <calls> ] [ -d <dispatcher> ] [ -n <numbers> "
"]\n"
" [ -t <table> ] [ -s <strace> ]\n"
"\n"
"The command is able to generate several syscalls related source "
"files.\n"
"\n"
" <calls> - Output: The assembly source file "
"implementing the\n"
" actual syscalls.\n"
" <dispatcher> - Output: The C source file to be included by "
"the\n"
" syscall dispatcher source file.\n"
" <numbers> - Output: The C/assembly include files "
"defining the\n"
" syscall numbers.\n"
" <table> - Output: A C source file containing an array "
"with\n"
" infos about the syscalls\n"
" <strace> - Output: A C source file for strace "
"support.\n");
}
Type::Type(const char* name, int size, int usedSize,
const char* alignmentTypeName)
:
fName(name),
fSize(size),
fUsedSize(usedSize),
fAlignmentType(alignmentTypeName)
{
}
Parameter::Parameter(const char* typeName, const char* parameterName,
int size, int usedSize, int offset, const char* alignmentTypeName)
:
Type(typeName, size, usedSize, alignmentTypeName),
fParameterName(parameterName),
fOffset(offset)
{
}
struct Syscall::ParameterVector : public vector<Parameter*> {
};
Syscall::Syscall(const char* name, const char* kernelName)
:
fName(name),
fKernelName(kernelName),
fReturnType(NULL),
fParameters(new ParameterVector)
{
}
Syscall::~Syscall()
{
delete fReturnType;
int count = CountParameters();
for (int i = 0; i < count; i++)
delete ParameterAt(i);
delete fParameters;
}
int
Syscall::CountParameters() const
{
return fParameters->size();
}
Parameter*
Syscall::ParameterAt(int index) const
{
if (index < 0 || index >= CountParameters())
return NULL;
return (*fParameters)[index];
}
Parameter*
Syscall::LastParameter() const
{
return ParameterAt(CountParameters() - 1);
}
void
Syscall::SetReturnType(int size, const char* name)
{
int usedSize = (size + kReturnTypeAlignmentSize - 1)
/ kReturnTypeAlignmentSize * kReturnTypeAlignmentSize;
const char* alignmentType
= size != usedSize && size < kReturnTypeAlignmentSize
? kReturnTypeAlignmentType : 0;
SetReturnType(name, size, usedSize, alignmentType);
}
Type*
Syscall::SetReturnType(const char* name, int size, int usedSize,
const char* alignmentTypeName)
{
delete fReturnType;
return fReturnType = new Type(name, size, usedSize, alignmentTypeName);
}
Parameter*
Syscall::AddParameter(const char* typeName, const char* parameterName,
int size, int usedSize, int offset, const char* alignmentTypeName)
{
Parameter* parameter = new Parameter(typeName, parameterName, size,
usedSize, offset, alignmentTypeName);
fParameters->push_back(parameter);
return parameter;
}
void
Syscall::AddParameter(int size, const char* typeName, const char* parameterName)
{
int offset = 0;
if (Parameter* previous = LastParameter())
offset = previous->Offset() + previous->UsedSize();
if (size >= kLongParameterAlignmentSize) {
if ((offset % kLongParameterAlignmentSize) != 0) {
offset += kLongParameterAlignmentSize
- offset % kLongParameterAlignmentSize;
}
}
int usedSize = (size + kParameterAlignmentSize - 1)
/ kParameterAlignmentSize * kParameterAlignmentSize;
const char* alignmentType
= size != usedSize && size < kParameterAlignmentSize
? kParameterAlignmentType : 0;
AddParameter(typeName, parameterName, size, usedSize, offset,
alignmentType);
}
struct SyscallVector::_SyscallVector : public vector<Syscall*> {
};
SyscallVector::SyscallVector()
:
fSyscalls(new _SyscallVector)
{
}
SyscallVector::~SyscallVector()
{
int count = CountSyscalls();
for (int i = 0; i < count; i++)
delete SyscallAt(i);
delete fSyscalls;
}
SyscallVector*
SyscallVector::Create()
{
return new SyscallVector;
}
int
SyscallVector::CountSyscalls() const
{
return fSyscalls->size();
}
Syscall*
SyscallVector::SyscallAt(int index) const
{
if (index < 0 || index >= CountSyscalls())
return NULL;
return (*fSyscalls)[index];
}
Syscall*
SyscallVector::CreateSyscall(const char* name, const char* kernelName)
{
Syscall* syscall = new Syscall(name, kernelName);
fSyscalls->push_back(syscall);
return syscall;
}
class Main {
public:
int Run(int argc, char** argv)
{
const char* syscallsFile = NULL;
const char* dispatcherFile = NULL;
const char* numbersFile = NULL;
const char* tableFile = NULL;
const char* straceFile = NULL;
for (int argi = 1; argi < argc; argi++) {
string arg(argv[argi]);
if (arg == "-h" || arg == "--help") {
print_usage(false);
return 0;
} else if (arg == "-c") {
if (argi + 1 >= argc) {
print_usage(true);
return 1;
}
syscallsFile = argv[++argi];
} else if (arg == "-d") {
if (argi + 1 >= argc) {
print_usage(true);
return 1;
}
dispatcherFile = argv[++argi];
} else if (arg == "-n") {
if (argi + 1 >= argc) {
print_usage(true);
return 1;
}
numbersFile = argv[++argi];
} else if (arg == "-t") {
if (argi + 1 >= argc) {
print_usage(true);
return 1;
}
tableFile = argv[++argi];
} else if (arg == "-s") {
if (argi + 1 >= argc) {
print_usage(true);
return 1;
}
straceFile = argv[++argi];
} else {
print_usage(true);
return 1;
}
}
fSyscallVector = create_syscall_vector();
fSyscallCount = fSyscallVector->CountSyscalls();
if (!syscallsFile && !dispatcherFile && !numbersFile && !tableFile
&& !straceFile) {
printf("Found %d syscalls.\n", fSyscallCount);
return 0;
}
if (syscallsFile)
_WriteSyscallsFile(syscallsFile);
if (dispatcherFile)
_WriteDispatcherFile(dispatcherFile);
if (numbersFile)
_WriteNumbersFile(numbersFile);
if (tableFile)
_WriteTableFile(tableFile);
if (straceFile)
_WriteSTraceFile(straceFile);
return 0;
}
void _WriteSyscallsFile(const char* filename)
{
ofstream file(filename, ofstream::out | ofstream::trunc);
if (!file.is_open())
throw IOException(string("Failed to open `") + filename + "'.");
for (int i = 0; i < fSyscallCount; i++) {
const Syscall* syscall = fSyscallVector->SyscallAt(i);
int paramCount = syscall->CountParameters();
int paramSize = 0;
for (int k = 0; k < paramCount; k++) {
const Parameter* parameter = syscall->ParameterAt(k);
int size = parameter->UsedSize();
paramSize += (size + 3) / 4 * 4;
}
file << "SYSCALL" << (paramSize / 4) << "("
<< syscall->Name() << ", " << i << ")" << endl;
}
}
void _WriteDispatcherFile(const char* filename)
{
ofstream file(filename, ofstream::out | ofstream::trunc);
if (!file.is_open())
throw IOException(string("Failed to open `") + filename + "'.");
for (int i = 0; i < fSyscallCount; i++) {
const Syscall* syscall = fSyscallVector->SyscallAt(i);
file << "case " << i << ":" << endl;
file << "\t";
if (string(syscall->ReturnType()->TypeName()) != "void")
file << "*_returnValue = ";
file << syscall->KernelName() << "(";
int paramCount = syscall->CountParameters();
if (paramCount > 0) {
Parameter* parameter = syscall->ParameterAt(0);
if (parameter->AlignmentTypeName()) {
file << "(" << parameter->TypeName() << ")*("
<< parameter->AlignmentTypeName()
<< "*)args";
} else {
file << "*(" << _GetPointerType(parameter->TypeName())
<< ")args";
}
for (int k = 1; k < paramCount; k++) {
parameter = syscall->ParameterAt(k);
if (parameter->AlignmentTypeName()) {
file << ", (" << parameter->TypeName() << ")*("
<< parameter->AlignmentTypeName()
<< "*)((char*)args + " << parameter->Offset()
<< ")";
} else {
file << ", *(" << _GetPointerType(parameter->TypeName())
<< ")((char*)args + " << parameter->Offset()
<< ")";
}
}
}
file << ");" << endl;
file << "\tbreak;" << endl;
}
}
void _WriteNumbersFile(const char* filename)
{
ofstream file(filename, ofstream::out | ofstream::trunc);
if (!file.is_open())
throw IOException(string("Failed to open `") + filename + "'.");
const char* prefix = "_user_";
size_t prefixLen = strlen(prefix);
for (int i = 0; i < fSyscallCount; i++) {
const Syscall* syscall = fSyscallVector->SyscallAt(i);
string name(syscall->KernelName());
if (name.find(prefix) != 0)
throw Exception(string("Bad kernel name: `") + name + "'.");
name = string(name, prefixLen);
string defineName;
for (int k = 0; k < (int)name.length(); k++)
defineName += toupper(name[k]);
file << "#define SYSCALL_" << defineName << " " << i << endl;
}
}
void _WriteTableFile(const char* filename)
{
ofstream file(filename, ofstream::out | ofstream::trunc);
if (!file.is_open())
throw IOException(string("Failed to open `") + filename + "'.");
file << "#define SYSCALL_COUNT " << fSyscallCount << endl;
file << endl;
file << "#ifndef _ASSEMBLER" << endl;
file << endl;
file << "#include <TypeConstants.h>" << endl;
file << endl;
file << "const int kSyscallCount = SYSCALL_COUNT;" << endl;
file << endl;
file << "const syscall_info kSyscallInfos[] = {" << endl;
for (int i = 0; i < fSyscallCount; i++) {
const Syscall* syscall = fSyscallVector->SyscallAt(i);
int paramSize = 0;
if (Parameter* parameter = syscall->LastParameter())
paramSize = parameter->Offset() + parameter->UsedSize();
file << "\t{ (void *)" << syscall->KernelName() << ", "
<< paramSize << " }," << endl;
}
file << "};" << endl;
file << endl;
file << "const extended_syscall_info kExtendedSyscallInfos[] = {"
<< endl;
for (int i = 0; i < fSyscallCount; i++) {
const Syscall* syscall = fSyscallVector->SyscallAt(i);
int paramCount = syscall->CountParameters();
file << "\t{" << endl;
file << "\t\t\"" << syscall->Name() << "\", " << paramCount << ","
<< endl;
Type* returnType = syscall->ReturnType();
file << "\t\t{ " << returnType->Size() << ", "
<< returnType->UsedSize() << ", "
<< _GetTypeCode(returnType) << " }," << endl;
file << "\t\t{" << endl;
for (int k = 0; k < paramCount; k++) {
const Parameter* parameter = syscall->ParameterAt(k);
file << "\t\t\t{ " << parameter->Offset() << ", "
<< parameter->Size() << ", "
<< parameter->UsedSize() << ", "
<< _GetTypeCode(parameter) << " }," << endl;
}
file << "\t\t}" << endl;
file << "\t}," << endl;
}
file << "};" << endl;
file << "#endif // _ASSEMBLER" << endl;
}
void _WriteSTraceFile(const char* filename)
{
ofstream file(filename, ofstream::out | ofstream::trunc);
if (!file.is_open())
throw IOException(string("Failed to open `") + filename + "'.");
file << "void" << endl
<< "GET_SYSCALLS(vector<Syscall*> &syscalls)" << endl
<< "{" << endl
<< "\tSyscall *syscall;" << endl
<< "\tTypeHandler *handler;" << endl
<< "(void)syscall;" << endl
<< "(void)handler;" << endl;
int chunkSize = (fSyscallCount + 19) / 20;
for (int i = 0; i < fSyscallCount; i++) {
const Syscall* syscall = fSyscallVector->SyscallAt(i);
if (i % chunkSize == 0) {
file << endl;
if (i > 0)
file << "#endif" << endl;
file << "#ifdef SYSCALLS_CHUNK_" << (i / chunkSize) << endl;
}
file << endl;
file << "\t// " << syscall->Name() << endl;
const char* returnType = syscall->ReturnType()->TypeName();
file << "\thandler = TypeHandlerFactory<"
<< returnType
<< ">::Create();" << endl;
file << "\tsyscall = new Syscall(\"" << syscall->Name() << "\", "
<< "\"" << returnType << "\", "<< "handler);" << endl;
file << "\tsyscalls.push_back(syscall);" << endl;
int paramCount = syscall->CountParameters();
for (int k = 0; k < paramCount; k++) {
const Parameter* parameter = syscall->ParameterAt(k);
file << "\thandler = TypeHandlerFactory<"
<< parameter->TypeName() << ">::Create();" << endl;
file << "\tsyscall->AddParameter(\""
<< parameter->ParameterName() << "\", "
<< parameter->Offset() << ", \"" << parameter->TypeName()
<< "\", handler);" << endl;
}
}
file << "#endif" << endl;
file << "}" << endl;
}
static string _GetPointerType(const char* type)
{
const char* parenthesis = strchr(type, ')');
if (!parenthesis)
return string(type) + "*";
return string(type, parenthesis - type) + "*" + parenthesis;
}
static string _GetTypeCode(const Type* type)
{
const char* typeName = type->TypeName();
if (strchr(typeName, '*')) {
if ((_GetTypeCodeTokenize(typeName) == "const"
&& _GetTypeCodeTokenize(typeName) == "char"
&& _GetTypeCodeTokenize(typeName) == "*"
&& _GetTypeCodeTokenize(typeName) == "")
|| (_GetTypeCodeTokenize(typeName) == "char"
&& _GetTypeCodeTokenize(typeName) == "const"
&& _GetTypeCodeTokenize(typeName) == "*"
&& _GetTypeCodeTokenize(typeName) == "")) {
return "B_STRING_TYPE";
}
return "B_POINTER_TYPE";
} else {
switch (type->Size()) {
case 1:
return "B_INT8_TYPE";
case 2:
return "B_INT16_TYPE";
case 4:
return "B_INT32_TYPE";
case 8:
return "B_INT64_TYPE";
default:
return "B_RAW_TYPE";
}
}
}
static string _GetTypeCodeTokenize(const char*& type)
{
while (*type != '\0' && isspace(*type))
type++;
switch (*type) {
case '\0':
return "";
case '*':
case '(':
case ')':
case '&':
return string(type++, 1);
default:
{
if (*type != '_' && !isalpha(*type)) {
return string(type++, 1);
}
const char* start = type;
while (*type == '_' || isalnum(*type))
type++;
return string(start, type - start);
}
}
}
private:
SyscallVector* fSyscallVector;
int fSyscallCount;
};
int
main(int argc, char** argv)
{
try {
return Main().Run(argc, argv);
} catch (Exception &exception) {
fprintf(stderr, "%s\n", exception.what());
return 1;
}
}