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