• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python
2#
3# this tool is used to generate the syscall assmbler templates
4# to be placed into arch-x86/syscalls, as well as the content
5# of arch-x86/linux/_syscalls.h
6#
7
8import sys, os.path, glob, re, commands, filecmp, shutil
9
10from bionic_utils import *
11
12# set this to 1 if you want to generate thumb stubs
13gen_thumb_stubs = 0
14
15# set this to 1 if you want to generate ARM EABI stubs
16gen_eabi_stubs = 1
17
18# get the root Bionic directory, simply this script's dirname
19#
20bionic_root = find_bionic_root()
21if not bionic_root:
22    print "could not find the Bionic root directory. aborting"
23    sys.exit(1)
24
25if bionic_root[-1] != '/':
26    bionic_root += "/"
27
28print "bionic_root is %s" % bionic_root
29
30# temp directory where we store all intermediate files
31bionic_temp = "/tmp/bionic_gensyscalls/"
32
33# all architectures, update as you see fit
34all_archs = [ "arm", "x86", "sh" ]
35
36def make_dir( path ):
37    if not os.path.exists(path):
38        parent = os.path.dirname(path)
39        if parent:
40            make_dir(parent)
41        os.mkdir(path)
42
43def create_file( relpath ):
44    dir = os.path.dirname( bionic_temp + relpath )
45    make_dir(dir)
46    return open( bionic_temp + relpath, "w" )
47
48# x86 assembler templates for each syscall stub
49#
50
51x86_header = """/* autogenerated by gensyscalls.py */
52#include <sys/linux-syscalls.h>
53
54    .text
55    .type %(fname)s, @function
56    .globl %(fname)s
57    .align 4
58
59%(fname)s:
60"""
61
62x86_registers = [ "%ebx", "%ecx", "%edx", "%esi", "%edi", "%ebp" ]
63
64x86_call = """    movl    $%(idname)s, %%eax
65    int     $0x80
66    cmpl    $-129, %%eax
67    jb      1f
68    negl    %%eax
69    pushl   %%eax
70    call    __set_errno
71    addl    $4, %%esp
72    orl     $-1, %%eax
731:
74"""
75
76x86_return = """    ret
77"""
78
79# ARM assembler templates for each syscall stub
80#
81arm_header = """/* autogenerated by gensyscalls.py */
82#include <machine/asm.h>
83#include <sys/linux-syscalls.h>
84
85ENTRY(%(fname)s)
86"""
87
88arm_footer = """\
89END(%(fname)s)
90"""
91
92arm_call_default = arm_header + """\
93    swi   #%(idname)s
94    movs    r0, r0
95    bxpl    lr
96    b       __set_syscall_errno
97""" + arm_footer
98
99arm_call_long = arm_header + """\
100    .save   {r4, r5, lr}
101    stmfd   sp!, {r4, r5, lr}
102    ldr     r4, [sp, #12]
103    ldr     r5, [sp, #16]
104    swi     # %(idname)s
105    ldmfd   sp!, {r4, r5, lr}
106    movs    r0, r0
107    bxpl    lr
108    b       __set_syscall_errno
109""" + arm_footer
110
111arm_eabi_call_default = arm_header + """\
112    .save   {r4, r7}
113    stmfd   sp!, {r4, r7}
114    ldr     r7, =%(idname)s
115    swi     #0
116    ldmfd   sp!, {r4, r7}
117    movs    r0, r0
118    bxpl    lr
119    b       __set_syscall_errno
120""" + arm_footer
121
122arm_eabi_call_long = arm_header + """\
123    mov     ip, sp
124    .save   {r4, r5, r6, r7}
125    stmfd   sp!, {r4, r5, r6, r7}
126    ldmfd   ip, {r4, r5, r6}
127    ldr     r7, =%(idname)s
128    swi     #0
129    ldmfd   sp!, {r4, r5, r6, r7}
130    movs    r0, r0
131    bxpl    lr
132    b       __set_syscall_errno
133""" + arm_footer
134
135# ARM thumb assembler templates for each syscall stub
136#
137thumb_header = """/* autogenerated by gensyscalls.py */
138    .text
139    .type %(fname)s, #function
140    .globl %(fname)s
141    .align 4
142    .thumb_func
143    .fnstart
144
145#define  __thumb__
146#include <sys/linux-syscalls.h>
147
148
149%(fname)s:
150"""
151
152thumb_call_default = thumb_header + """\
153    .save   {r7,lr}
154    push    {r7,lr}
155    ldr     r7, =%(idname)s
156    swi     #0
157    tst     r0, r0
158    bmi     1f
159    pop     {r7,pc}
1601:
161    neg     r0, r0
162    ldr     r1, =__set_errno
163    blx     r1
164    pop     {r7,pc}
165    .fnend
166"""
167
168thumb_call_long = thumb_header + """\
169    .save  {r4,r5,r7,lr}
170    push   {r4,r5,r7,lr}
171    ldr    r4, [sp,#16]
172    ldr    r5, [sp,#20]
173    ldr    r7, =%(idname)s
174    swi    #0
175    tst    r0, r0
176    bmi    1f
177    pop    {r4,r5,r7,pc}
1781:
179    neg    r0, r0
180    ldr    r1, =__set_errno
181    blx    r1
182    pop    {r4,r5,r7,pc}
183    .fnend
184"""
185
186# SuperH assembler templates for each syscall stub
187#
188superh_header = """/* autogenerated by gensyscalls.py */
189#include <sys/linux-syscalls.h>
190
191    .text
192    .type %(fname)s, @function
193    .globl %(fname)s
194    .align 4
195
196%(fname)s:
197"""
198
199superh_call_default = """
200    /* invoke trap */
201    mov.l   0f, r3  /* trap num */
202    trapa   #(%(numargs)s + 0x10)
203
204    /* check return value */
205    cmp/pz  r0
206    bt      %(idname)s_end
207
208    /* keep error number */
209    sts.l   pr, @-r15
210    mov.l   1f, r1
211    jsr     @r1
212    mov     r0, r4
213    lds.l   @r15+, pr
214
215%(idname)s_end:
216    rts
217    nop
218
219    .align  2
2200:  .long   %(idname)s
2211:  .long   __set_syscall_errno
222"""
223
224superh_5args_header = """
225    /* get ready for additonal arg */
226    mov.l   @r15, r0
227"""
228
229superh_6args_header = """
230    /* get ready for additonal arg */
231    mov.l   @r15, r0
232    mov.l   @(4, r15), r1
233"""
234
235superh_7args_header = """
236    /* get ready for additonal arg */
237    mov.l   @r15, r0
238    mov.l   @(4, r15), r1
239    mov.l   @(8, r15), r2
240"""
241
242
243def param_uses_64bits(param):
244    """Returns True iff a syscall parameter description corresponds
245       to a 64-bit type."""
246    param = param.strip()
247    # First, check that the param type begins with one of the known
248    # 64-bit types.
249    if not ( \
250       param.startswith("int64_t") or param.startswith("uint64_t") or \
251       param.startswith("loff_t") or param.startswith("off64_t") or \
252       param.startswith("long long") or param.startswith("unsigned long long") or
253       param.startswith("signed long long") ):
254           return False
255
256    # Second, check that there is no pointer type here
257    if param.find("*") >= 0:
258            return False
259
260    # Ok
261    return True
262
263def count_arm_param_registers(params):
264    """This function is used to count the number of register used
265       to pass parameters when invoking a thumb or ARM system call.
266       This is because the ARM EABI mandates that 64-bit quantities
267       must be passed in an even+odd register pair. So, for example,
268       something like:
269
270             foo(int fd, off64_t pos)
271
272       would actually need 4 registers:
273             r0 -> int
274             r1 -> unused
275             r2-r3 -> pos
276   """
277    count = 0
278    for param in params:
279        if param_uses_64bits(param):
280            if (count & 1) != 0:
281                count += 1
282            count += 2
283        else:
284            count += 1
285    return count
286
287def count_generic_param_registers(params):
288    count = 0
289    for param in params:
290        if param_uses_64bits(param):
291            count += 2
292        else:
293            count += 1
294    return count
295
296class State:
297    def __init__(self):
298        self.old_stubs = []
299        self.new_stubs = []
300        self.other_files = []
301        self.syscalls = []
302
303    def x86_genstub(self, fname, numparams, idname):
304        t = { "fname"  : fname,
305              "idname" : idname }
306
307        result     = x86_header % t
308        stack_bias = 4
309        for r in range(numparams):
310            result     += "    pushl   " + x86_registers[r] + "\n"
311            stack_bias += 4
312
313        for r in range(numparams):
314            result += "    mov     %d(%%esp), %s" % (stack_bias+r*4, x86_registers[r]) + "\n"
315
316        result += x86_call % t
317
318        for r in range(numparams):
319            result += "    popl    " + x86_registers[numparams-r-1] + "\n"
320
321        result += x86_return
322        return result
323
324    def x86_genstub_cid(self, fname, numparams, idname, cid):
325        # We'll ignore numparams here because in reality, if there is a
326        # dispatch call (like a socketcall syscall) there are actually
327        # only 2 arguments to the syscall and 2 regs we have to save:
328        #   %ebx <--- Argument 1 - The call id of the needed vectored
329        #                          syscall (socket, bind, recv, etc)
330        #   %ecx <--- Argument 2 - Pointer to the rest of the arguments
331        #                          from the original function called (socket())
332        t = { "fname"  : fname,
333              "idname" : idname }
334
335        result = x86_header % t
336        stack_bias = 4
337
338        # save the regs we need
339        result += "    pushl   %ebx" + "\n"
340        stack_bias += 4
341        result += "    pushl   %ecx" + "\n"
342        stack_bias += 4
343
344        # set the call id (%ebx)
345        result += "    mov     $%d, %%ebx" % (cid) + "\n"
346
347        # set the pointer to the rest of the args into %ecx
348        result += "    mov     %esp, %ecx" + "\n"
349        result += "    addl    $%d, %%ecx" % (stack_bias) + "\n"
350
351        # now do the syscall code itself
352        result += x86_call % t
353
354        # now restore the saved regs
355        result += "    popl    %ecx" + "\n"
356        result += "    popl    %ebx" + "\n"
357
358        # epilog
359        result += x86_return
360        return result
361
362    def arm_genstub(self,fname, flags, idname):
363        t = { "fname"  : fname,
364              "idname" : idname }
365        if flags:
366            numargs = int(flags)
367            if numargs > 4:
368                return arm_call_long % t
369        return arm_call_default % t
370
371
372    def arm_eabi_genstub(self,fname, flags, idname):
373        t = { "fname"  : fname,
374              "idname" : idname }
375        if flags:
376            numargs = int(flags)
377            if numargs > 4:
378                return arm_eabi_call_long % t
379        return arm_eabi_call_default % t
380
381
382    def thumb_genstub(self,fname, flags, idname):
383        t = { "fname"  : fname,
384              "idname" : idname }
385        if flags:
386            numargs = int(flags)
387            if numargs > 4:
388                return thumb_call_long % t
389        return thumb_call_default % t
390
391
392    def superh_genstub(self, fname, flags, idname):
393        numargs = int(flags)
394        t = { "fname"  : fname,
395              "idname" : idname,
396              "numargs" : numargs }
397        superh_call = superh_header
398        if flags:
399            if numargs == 5:
400                superh_call += superh_5args_header
401            if numargs == 6:
402                superh_call += superh_6args_header
403            if numargs == 7:
404                superh_call += superh_7args_header
405        superh_call += superh_call_default
406        return superh_call % t
407
408
409    def process_file(self,input):
410        parser = SysCallsTxtParser()
411        parser.parse_file(input)
412        self.syscalls = parser.syscalls
413        parser = None
414
415        for t in self.syscalls:
416            syscall_func   = t["func"]
417            syscall_params = t["params"]
418            syscall_name   = t["name"]
419
420            if t["id"] >= 0:
421                num_regs = count_arm_param_registers(syscall_params)
422                if gen_thumb_stubs:
423                    t["asm-thumb"] = self.thumb_genstub(syscall_func,num_regs,"__NR_"+syscall_name)
424                else:
425                    if gen_eabi_stubs:
426                        t["asm-arm"]   = self.arm_eabi_genstub(syscall_func,num_regs,"__NR_"+syscall_name)
427                    else:
428                        t["asm-arm"]   = self.arm_genstub(syscall_func,num_regs,"__NR_"+syscall_name)
429
430            if t["id2"] >= 0:
431                num_regs = count_generic_param_registers(syscall_params)
432                if t["cid"] >= 0:
433                    t["asm-x86"] = self.x86_genstub_cid(syscall_func, num_regs, "__NR_"+syscall_name, t["cid"])
434                else:
435                    t["asm-x86"] = self.x86_genstub(syscall_func, num_regs, "__NR_"+syscall_name)
436            elif t["cid"] >= 0:
437                E("cid for dispatch syscalls is only supported for x86 in "
438                  "'%s'" % syscall_name)
439                return
440            if t["id3"] >= 0:
441                num_regs = count_generic_param_registers(syscall_params)
442                t["asm-sh"] = self.superh_genstub(syscall_func,num_regs,"__NR_"+syscall_name)
443
444
445
446    def gen_NR_syscall(self,fp,name,id):
447        fp.write( "#define __NR_%-25s    (__NR_SYSCALL_BASE + %d)\n" % (name,id) )
448
449    # now dump the content of linux/_syscalls.h
450    def gen_linux_syscalls_h(self):
451        path = "include/sys/linux-syscalls.h"
452        D( "generating "+path )
453        fp = create_file( path )
454        fp.write( "/* auto-generated by gensyscalls.py, do not touch */\n" )
455        fp.write( "#ifndef _BIONIC_LINUX_SYSCALLS_H_\n\n" )
456        fp.write( "#if !defined __ASM_ARM_UNISTD_H && !defined __ASM_I386_UNISTD_H\n" )
457        fp.write( "#if defined __arm__ && !defined __ARM_EABI__ && !defined __thumb__\n" )
458        fp.write( "  #  define __NR_SYSCALL_BASE  0x900000\n" )
459        fp.write( "  #else\n" )
460        fp.write( "  #  define  __NR_SYSCALL_BASE  0\n" )
461        fp.write( "  #endif\n\n" )
462
463        # first, all common syscalls
464        for sc in self.syscalls:
465            sc_id  = sc["id"]
466            sc_id2 = sc["id2"]
467            sc_name = sc["name"]
468            if sc_id == sc_id2 and sc_id >= 0:
469                self.gen_NR_syscall( fp, sc_name, sc_id )
470
471        # now, all arm-specific syscalls
472        fp.write( "\n#ifdef __arm__\n" );
473        for sc in self.syscalls:
474            sc_id  = sc["id"]
475            sc_id2 = sc["id2"]
476            sc_name = sc["name"]
477            if sc_id != sc_id2 and sc_id >= 0:
478                self.gen_NR_syscall( fp, sc_name, sc_id )
479        fp.write( "#endif\n" );
480
481        gen_syscalls = {}
482        # finally, all i386-specific syscalls
483        fp.write( "\n#ifdef __i386__\n" );
484        for sc in self.syscalls:
485            sc_id  = sc["id"]
486            sc_id2 = sc["id2"]
487            sc_name = sc["name"]
488            if sc_id != sc_id2 and sc_id2 >= 0 and sc_name not in gen_syscalls:
489                self.gen_NR_syscall( fp, sc_name, sc_id2 )
490                gen_syscalls[sc_name] = True
491        fp.write( "#endif\n" );
492
493        # all superh-specific syscalls
494        fp.write( "\n#if defined(__SH3__) || defined(__SH4__) \n" );
495        for sc in self.syscalls:
496            sc_id  = sc["id"]
497            sc_id2 = sc["id2"]
498            sc_id3 = sc["id3"]
499            sc_name = sc["name"]
500            if sc_id2 != sc_id3 and sc_id3 >= 0:
501                self.gen_NR_syscall( fp, sc_name, sc_id3 )
502            else:
503                if sc_id != sc_id2 and sc_id2 >= 0:
504                    self.gen_NR_syscall( fp, sc_name, sc_id2 )
505        fp.write( "#endif\n" );
506
507        fp.write( "\n#endif\n" )
508        fp.write( "\n#endif /* _BIONIC_LINUX_SYSCALLS_H_ */\n" );
509        fp.close()
510        self.other_files.append( path )
511
512
513    # now dump the content of linux/_syscalls.h
514    def gen_linux_unistd_h(self):
515        path = "include/sys/linux-unistd.h"
516        D( "generating "+path )
517        fp = create_file( path )
518        fp.write( "/* auto-generated by gensyscalls.py, do not touch */\n" )
519        fp.write( "#ifndef _BIONIC_LINUX_UNISTD_H_\n\n" );
520        fp.write( "#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n" )
521
522        for sc in self.syscalls:
523            fp.write( sc["decl"]+"\n" )
524
525        fp.write( "#ifdef __cplusplus\n}\n#endif\n" )
526        fp.write( "\n#endif /* _BIONIC_LINUX_UNISTD_H_ */\n" );
527        fp.close()
528        self.other_files.append( path )
529
530    # now dump the contents of syscalls.mk
531    def gen_arch_syscalls_mk(self, arch):
532        path = "arch-%s/syscalls.mk" % arch
533        D( "generating "+path )
534        fp = create_file( path )
535        fp.write( "# auto-generated by gensyscalls.py, do not touch\n" )
536        fp.write( "syscall_src := \n" )
537        arch_test = {
538            "arm": lambda x: x.has_key("asm-arm") or x.has_key("asm-thumb"),
539            "x86": lambda x: x.has_key("asm-x86"),
540            "sh": lambda x: x.has_key("asm-sh")
541        }
542
543        for sc in self.syscalls:
544            if arch_test[arch](sc):
545                fp.write("syscall_src += arch-%s/syscalls/%s.S\n" %
546                         (arch, sc["func"]))
547        fp.close()
548        self.other_files.append( path )
549
550    # now generate each syscall stub
551    def gen_syscall_stubs(self):
552        for sc in self.syscalls:
553            if sc.has_key("asm-arm") and 'arm' in all_archs:
554                fname = "arch-arm/syscalls/%s.S" % sc["func"]
555                D2( ">>> generating "+fname )
556                fp = create_file( fname )
557                fp.write(sc["asm-arm"])
558                fp.close()
559                self.new_stubs.append( fname )
560
561            if sc.has_key("asm-thumb") and 'arm' in all_archs:
562                fname = "arch-arm/syscalls/%s.S" % sc["func"]
563                D2( ">>> generating "+fname )
564                fp = create_file( fname )
565                fp.write(sc["asm-thumb"])
566                fp.close()
567                self.new_stubs.append( fname )
568
569            if sc.has_key("asm-x86") and 'x86' in all_archs:
570                fname = "arch-x86/syscalls/%s.S" % sc["func"]
571                D2( ">>> generating "+fname )
572                fp = create_file( fname )
573                fp.write(sc["asm-x86"])
574                fp.close()
575                self.new_stubs.append( fname )
576
577            if sc.has_key("asm-sh"):
578                fname = "arch-sh/syscalls/%s.S" % sc["func"]
579                D2( ">>> generating "+fname )
580                fp = create_file( fname )
581                fp.write(sc["asm-sh"])
582                fp.close()
583                self.new_stubs.append( fname )
584
585
586    def  regenerate(self):
587        D( "scanning for existing architecture-specific stub files" )
588
589        bionic_root_len = len(bionic_root)
590
591        for arch in all_archs:
592            arch_path = bionic_root + "arch-" + arch
593            D( "scanning " + arch_path )
594            files = glob.glob( arch_path + "/syscalls/*.S" )
595            for f in files:
596                self.old_stubs.append( f[bionic_root_len:] )
597
598        D( "found %d stub files" % len(self.old_stubs) )
599
600        if not os.path.exists( bionic_temp ):
601            D( "creating %s" % bionic_temp )
602            os.mkdir( bionic_temp )
603
604#        D( "p4 editing source files" )
605#        for arch in all_archs:
606#            commands.getoutput( "p4 edit " + arch + "/syscalls/*.S " )
607#            commands.getoutput( "p4 edit " + arch + "/syscalls.mk" )
608#        commands.getoutput( "p4 edit " + bionic_root + "include/sys/linux-syscalls.h" )
609
610        D( "re-generating stubs and support files" )
611
612        self.gen_linux_syscalls_h()
613        for arch in all_archs:
614            self.gen_arch_syscalls_mk(arch)
615        self.gen_linux_unistd_h()
616        self.gen_syscall_stubs()
617
618        D( "comparing files" )
619        adds    = []
620        edits   = []
621
622        for stub in self.new_stubs + self.other_files:
623            if not os.path.exists( bionic_root + stub ):
624                # new file, git add it
625                D( "new file:     " + stub)
626                adds.append( bionic_root + stub )
627                shutil.copyfile( bionic_temp + stub, bionic_root + stub )
628
629            elif not filecmp.cmp( bionic_temp + stub, bionic_root + stub ):
630                D( "changed file: " + stub)
631                edits.append( stub )
632
633        deletes = []
634        for stub in self.old_stubs:
635            if not stub in self.new_stubs:
636                D( "deleted file: " + stub)
637                deletes.append( bionic_root + stub )
638
639
640        if adds:
641            commands.getoutput("git add " + " ".join(adds))
642        if deletes:
643            commands.getoutput("git rm " + " ".join(deletes))
644        if edits:
645            for file in edits:
646                shutil.copyfile( bionic_temp + file, bionic_root + file )
647            commands.getoutput("git add " +
648                               " ".join((bionic_root + file) for file in edits))
649
650        commands.getoutput("git add %s%s" % (bionic_root,"SYSCALLS.TXT"))
651
652        if (not adds) and (not deletes) and (not edits):
653            D("no changes detected!")
654        else:
655            D("ready to go!!")
656
657D_setlevel(1)
658
659state = State()
660state.process_file(bionic_root+"SYSCALLS.TXT")
661state.regenerate()
662