#!/usr/bin/env python

'''
Generate syscall tables from the Linux source. Used to generate
manticore/platforms/linux_syscalls.py.

This fetches the tables from kernel.org.

Usage:

    ./extract_syscalls.py [--linux_version linux_version] linux_syscalls.py

'''

import os
import sys
import urllib
import argparse

base_url = 'https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/{}?id=refs/tags/v{}'

arch_tables = {
    'armv7': 'arch/arm/tools/syscall.tbl',
    'i386':  'arch/x86/entry/syscalls/syscall_32.tbl',
    'amd64': 'arch/x86/entry/syscalls/syscall_64.tbl',
}

__ARM_NR_BASE = 0xf0000
additional_syscalls = {
    'armv7': [
        ('sys_ARM_NR_breakpoint', __ARM_NR_BASE + 1),
        ('sys_ARM_NR_cacheflush', __ARM_NR_BASE + 2),
        ('sys_ARM_NR_usr26', __ARM_NR_BASE + 3),
        ('sys_ARM_NR_usr32', __ARM_NR_BASE + 4),
        ('sys_ARM_NR_set_tls', __ARM_NR_BASE + 5),
    ]
}



if __name__=='__main__':
    parser = argparse.ArgumentParser(description='Generate syscall tables')
    parser.add_argument('output', help='Python output to generate tables')
    parser.add_argument('--linux_version', help='Major version of the Linux kernel to use', default='4.11')
    args = parser.parse_args()

    output = open(args.output, 'w+')
    output.write('#\n#\n# AUTOGENERATED, DO NOT EDIT\n#\n')
    output.write('# From version: {}\n#\n\n'.format(args.linux_version))

    for arch, path in arch_tables.items():
        url = base_url.format(path, args.linux_version)
        tbl = urllib.urlopen(url)

        if tbl.code // 100 != 2:
            sys.stderr.write("Failed retrieving table; check version and connection.\n")
            sys.stderr.write("Url: {}\n".format(url))
            sys.exit(1)

        output.write('{} = {{\n'.format(arch))
        for line in tbl.readlines():
            line = line.strip()
            if line.startswith('#'):
                continue
            columns = line.split()
            if len(columns) < 4:
                continue
            num, abi, name, entry = columns[:4]
            output.write('    {}: "{}",\n'.format(num, entry))
        for entry, num in additional_syscalls.get(arch, {}):
            output.write('    {}: "{}",\n'.format(num, entry))
        output.write('}\n')
        

