gensyscalls.py revision 4e468ed2eb86a2406e14f1eca82072ee501d05fd
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" ]
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 <sys/linux-syscalls.h>
83
84    .text
85    .type %(fname)s, #function
86    .globl %(fname)s
87    .align 4
88    .fnstart
89
90%(fname)s:
91"""
92
93arm_call_default = arm_header + """\
94    swi   #%(idname)s
95    movs    r0, r0
96    bxpl    lr
97    b       __set_syscall_errno
98    .fnend
99"""
100
101arm_call_long = arm_header + """\
102    .save   {r4, r5, lr}
103    stmfd   sp!, {r4, r5, lr}
104    ldr     r4, [sp, #12]
105    ldr     r5, [sp, #16]
106    swi     # %(idname)s
107    ldmfd   sp!, {r4, r5, lr}
108    movs    r0, r0
109    bxpl    lr
110    b       __set_syscall_errno
111    .fnend
112"""
113
114arm_eabi_call_default = arm_header + """\
115    .save   {r4, r7}
116    stmfd   sp!, {r4, r7}
117    ldr     r7, =%(idname)s
118    swi     #0
119    ldmfd   sp!, {r4, r7}
120    movs    r0, r0
121    bxpl    lr
122    b       __set_syscall_errno
123    .fnend
124"""
125
126arm_eabi_call_long = arm_header + """\
127    mov     ip, sp
128    .save   {r4, r5, r6, r7}
129    stmfd   sp!, {r4, r5, r6, r7}
130    ldmfd   ip, {r4, r5, r6}
131    ldr     r7, =%(idname)s
132    swi     #0
133    ldmfd   sp!, {r4, r5, r6, r7}
134    movs    r0, r0
135    bxpl    lr
136    b       __set_syscall_errno
137    .fnend
138"""
139
140# ARM thumb assembler templates for each syscall stub
141#
142thumb_header = """/* autogenerated by gensyscalls.py */
143    .text
144    .type %(fname)s, #function
145    .globl %(fname)s
146    .align 4
147    .thumb_func
148    .fnstart
149
150#define  __thumb__
151#include <sys/linux-syscalls.h>
152
153
154%(fname)s:
155"""
156
157thumb_call_default = thumb_header + """\
158    .save   {r7,lr}
159    push    {r7,lr}
160    ldr     r7, =%(idname)s
161    swi     #0
162    tst     r0, r0
163    bmi     1f
164    pop     {r7,pc}
1651:
166    neg     r0, r0
167    ldr     r1, =__set_errno
168    blx     r1
169    pop     {r7,pc}
170    .fnend
171"""
172
173thumb_call_long = thumb_header + """\
174    .save  {r4,r5,r7,lr}
175    push   {r4,r5,r7,lr}
176    ldr    r4, [sp,#16]
177    ldr    r5, [sp,#20]
178    ldr    r7, =%(idname)s
179    swi    #0
180    tst    r0, r0
181    bmi    1f
182    pop    {r4,r5,r7,pc}
1831:
184    neg    r0, r0
185    ldr    r1, =__set_errno
186    blx    r1
187    pop    {r4,r5,r7,pc}
188    .fnend
189"""
190
191
192class State:
193    def __init__(self):
194        self.old_stubs = []
195        self.new_stubs = []
196        self.other_files = []
197        self.syscalls = []
198
199    def x86_genstub(self, fname, numparams, idname):
200        t = { "fname"  : fname,
201              "idname" : idname }
202
203        result     = x86_header % t
204        stack_bias = 4
205        for r in range(numparams):
206            result     += "    pushl   " + x86_registers[r] + "\n"
207            stack_bias += 4
208
209        for r in range(numparams):
210            result += "    mov     %d(%%esp), %s" % (stack_bias+r*4, x86_registers[r]) + "\n"
211
212        result += x86_call % t
213
214        for r in range(numparams):
215            result += "    popl    " + x86_registers[numparams-r-1] + "\n"
216
217        result += x86_return
218        return result
219
220    def x86_genstub_cid(self, fname, numparams, idname, cid):
221        # We'll ignore numparams here because in reality, if there is a
222        # dispatch call (like a socketcall syscall) there are actually
223        # only 2 arguments to the syscall and 2 regs we have to save:
224        #   %ebx <--- Argument 1 - The call id of the needed vectored
225        #                          syscall (socket, bind, recv, etc)
226        #   %ecx <--- Argument 2 - Pointer to the rest of the arguments
227        #                          from the original function called (socket())
228        t = { "fname"  : fname,
229              "idname" : idname }
230
231        result = x86_header % t
232        stack_bias = 4
233
234        # save the regs we need
235        result += "    pushl   %ebx" + "\n"
236        stack_bias += 4
237        result += "    pushl   %ecx" + "\n"
238        stack_bias += 4
239
240        # set the call id (%ebx)
241        result += "    mov     $%d, %%ebx" % (cid) + "\n"
242
243        # set the pointer to the rest of the args into %ecx
244        result += "    mov     %esp, %ecx" + "\n"
245        result += "    addl    $%d, %%ecx" % (stack_bias) + "\n"
246
247        # now do the syscall code itself
248        result += x86_call % t
249
250        # now restore the saved regs
251        result += "    popl    %ecx" + "\n"
252        result += "    popl    %ebx" + "\n"
253
254        # epilog
255        result += x86_return
256        return result
257
258    def arm_genstub(self,fname, flags, idname):
259        t = { "fname"  : fname,
260              "idname" : idname }
261        if flags:
262            numargs = int(flags)
263            if numargs > 4:
264                return arm_call_long % t
265        return arm_call_default % t
266
267
268    def arm_eabi_genstub(self,fname, flags, idname):
269        t = { "fname"  : fname,
270              "idname" : idname }
271        if flags:
272            numargs = int(flags)
273            if numargs > 4:
274                return arm_eabi_call_long % t
275        return arm_eabi_call_default % t
276
277
278    def thumb_genstub(self,fname, flags, idname):
279        t = { "fname"  : fname,
280              "idname" : idname }
281        if flags:
282            numargs = int(flags)
283            if numargs > 4:
284                return thumb_call_long % t
285        return thumb_call_default % t
286
287
288    def process_file(self,input):
289        parser = SysCallsTxtParser()
290        parser.parse_file(input)
291        self.syscalls = parser.syscalls
292        parser = None
293
294        for t in self.syscalls:
295            syscall_func   = t["func"]
296            syscall_params = t["params"]
297            syscall_name   = t["name"]
298
299            if t["id"] >= 0:
300                if gen_thumb_stubs:
301                    t["asm-thumb"] = self.thumb_genstub(syscall_func,len(syscall_params),"__NR_"+syscall_name)
302                else:
303                    if gen_eabi_stubs:
304                        t["asm-arm"]   = self.arm_eabi_genstub(syscall_func,len(syscall_params),"__NR_"+syscall_name)
305                    else:
306                        t["asm-arm"]   = self.arm_genstub(syscall_func,len(syscall_params),"__NR_"+syscall_name)
307
308            if t["id2"] >= 0:
309                if t["cid"] >= 0:
310                    t["asm-x86"] = self.x86_genstub_cid(syscall_func, len(syscall_params), "__NR_"+syscall_name, t["cid"])
311                else:
312                    t["asm-x86"] = self.x86_genstub(syscall_func,len(syscall_params),"__NR_"+syscall_name)
313            elif t["cid"] >= 0:
314                E("cid for dispatch syscalls is only supported for x86 in "
315                  "'%s'" % syscall_name)
316                return
317
318
319    def gen_NR_syscall(self,fp,name,id):
320        fp.write( "#define __NR_%-25s    (__NR_SYSCALL_BASE + %d)\n" % (name,id) )
321
322    # now dump the content of linux/_syscalls.h
323    def gen_linux_syscalls_h(self):
324        path = "include/sys/linux-syscalls.h"
325        D( "generating "+path )
326        fp = create_file( path )
327        fp.write( "/* auto-generated by gensyscalls.py, do not touch */\n" )
328        fp.write( "#ifndef _BIONIC_LINUX_SYSCALLS_H_\n\n" )
329        fp.write( "#if !defined __ASM_ARM_UNISTD_H && !defined __ASM_I386_UNISTD_H\n" )
330        fp.write( "#if defined __arm__ && !defined __ARM_EABI__ && !defined __thumb__\n" )
331        fp.write( "  #  define __NR_SYSCALL_BASE  0x900000\n" )
332        fp.write( "  #else\n" )
333        fp.write( "  #  define  __NR_SYSCALL_BASE  0\n" )
334        fp.write( "  #endif\n\n" )
335
336        # first, all common syscalls
337        for sc in self.syscalls:
338            sc_id  = sc["id"]
339            sc_id2 = sc["id2"]
340            sc_name = sc["name"]
341            if sc_id == sc_id2 and sc_id >= 0:
342                self.gen_NR_syscall( fp, sc_name, sc_id )
343
344        # now, all arm-specific syscalls
345        fp.write( "\n#ifdef __arm__\n" );
346        for sc in self.syscalls:
347            sc_id  = sc["id"]
348            sc_id2 = sc["id2"]
349            sc_name = sc["name"]
350            if sc_id != sc_id2 and sc_id >= 0:
351                self.gen_NR_syscall( fp, sc_name, sc_id )
352        fp.write( "#endif\n" );
353
354        gen_syscalls = {}
355        # finally, all i386-specific syscalls
356        fp.write( "\n#ifdef __i386__\n" );
357        for sc in self.syscalls:
358            sc_id  = sc["id"]
359            sc_id2 = sc["id2"]
360            sc_name = sc["name"]
361            if sc_id != sc_id2 and sc_id2 >= 0 and sc_name not in gen_syscalls:
362                self.gen_NR_syscall( fp, sc_name, sc_id2 )
363                gen_syscalls[sc_name] = True
364        fp.write( "#endif\n" );
365
366        fp.write( "\n#endif\n" )
367        fp.write( "\n#endif /* _BIONIC_LINUX_SYSCALLS_H_ */\n" );
368        fp.close()
369        self.other_files.append( path )
370
371
372    # now dump the content of linux/_syscalls.h
373    def gen_linux_unistd_h(self):
374        path = "include/sys/linux-unistd.h"
375        D( "generating "+path )
376        fp = create_file( path )
377        fp.write( "/* auto-generated by gensyscalls.py, do not touch */\n" )
378        fp.write( "#ifndef _BIONIC_LINUX_UNISTD_H_\n\n" );
379        fp.write( "#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n" )
380
381        for sc in self.syscalls:
382            fp.write( sc["decl"]+"\n" )
383
384        fp.write( "#ifdef __cplusplus\n}\n#endif\n" )
385        fp.write( "\n#endif /* _BIONIC_LINUX_UNISTD_H_ */\n" );
386        fp.close()
387        self.other_files.append( path )
388
389    # now dump the contents of syscalls.mk
390    def gen_arch_syscalls_mk(self, arch):
391        path = "arch-%s/syscalls.mk" % arch
392        D( "generating "+path )
393        fp = create_file( path )
394        fp.write( "# auto-generated by gensyscalls.py, do not touch\n" )
395        fp.write( "syscall_src := \n" )
396        arch_test = {
397            "arm": lambda x: x.has_key("asm-arm") or x.has_key("asm-thumb"),
398            "x86": lambda x: x.has_key("asm-x86")
399        }
400
401        for sc in self.syscalls:
402            if arch_test[arch](sc):
403                fp.write("syscall_src += arch-%s/syscalls/%s.S\n" %
404                         (arch, sc["func"]))
405        fp.close()
406        self.other_files.append( path )
407
408    # now generate each syscall stub
409    def gen_syscall_stubs(self):
410        for sc in self.syscalls:
411            if sc.has_key("asm-arm") and 'arm' in all_archs:
412                fname = "arch-arm/syscalls/%s.S" % sc["func"]
413                D( ">>> generating "+fname )
414                fp = create_file( fname )
415                fp.write(sc["asm-arm"])
416                fp.close()
417                self.new_stubs.append( fname )
418
419            if sc.has_key("asm-thumb") and 'arm' in all_archs:
420                fname = "arch-arm/syscalls/%s.S" % sc["func"]
421                D( ">>> generating "+fname )
422                fp = create_file( fname )
423                fp.write(sc["asm-thumb"])
424                fp.close()
425                self.new_stubs.append( fname )
426
427            if sc.has_key("asm-x86") and 'x86' in all_archs:
428                fname = "arch-x86/syscalls/%s.S" % sc["func"]
429                D( ">>> generating "+fname )
430                fp = create_file( fname )
431                fp.write(sc["asm-x86"])
432                fp.close()
433                self.new_stubs.append( fname )
434
435
436    def  regenerate(self):
437        D( "scanning for existing architecture-specific stub files" )
438
439        bionic_root_len = len(bionic_root)
440
441        for arch in all_archs:
442            arch_path = bionic_root + "arch-" + arch
443            D( "scanning " + arch_path )
444            files = glob.glob( arch_path + "/syscalls/*.S" )
445            for f in files:
446                self.old_stubs.append( f[bionic_root_len:] )
447
448        D( "found %d stub files" % len(self.old_stubs) )
449
450        if not os.path.exists( bionic_temp ):
451            D( "creating %s" % bionic_temp )
452            os.mkdir( bionic_temp )
453
454#        D( "p4 editing source files" )
455#        for arch in all_archs:
456#            commands.getoutput( "p4 edit " + arch + "/syscalls/*.S " )
457#            commands.getoutput( "p4 edit " + arch + "/syscalls.mk" )
458#        commands.getoutput( "p4 edit " + bionic_root + "include/sys/linux-syscalls.h" )
459
460        D( "re-generating stubs and support files" )
461
462        self.gen_linux_syscalls_h()
463        for arch in all_archs:
464            self.gen_arch_syscalls_mk(arch)
465        self.gen_linux_unistd_h()
466        self.gen_syscall_stubs()
467
468        D( "comparing files" )
469        adds    = []
470        edits   = []
471
472        for stub in self.new_stubs + self.other_files:
473            if not os.path.exists( bionic_root + stub ):
474                # new file, P4 add it
475                D( "new file:     " + stub)
476                adds.append( bionic_root + stub )
477                shutil.copyfile( bionic_temp + stub, bionic_root + stub )
478
479            elif not filecmp.cmp( bionic_temp + stub, bionic_root + stub ):
480                D( "changed file: " + stub)
481                edits.append( stub )
482
483        deletes = []
484        for stub in self.old_stubs:
485            if not stub in self.new_stubs:
486                D( "deleted file: " + stub)
487                deletes.append( bionic_root + stub )
488
489
490        if adds:
491            commands.getoutput("p4 add " + " ".join(adds))
492        if deletes:
493            commands.getoutput("p4 delete " + " ".join(deletes))
494        if edits:
495            commands.getoutput("p4 edit " +
496                               " ".join((bionic_root + file) for file in edits))
497            for file in edits:
498                shutil.copyfile( bionic_temp + file, bionic_root + file )
499
500        D("ready to go !!")
501
502D_setlevel(1)
503
504state = State()
505state.process_file(bionic_root+"SYSCALLS.TXT")
506state.regenerate()
507