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