gensyscalls.py revision c95eb57405d3d2f0e6cfab313aa74b1bad280452
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 mov ip, r7 115 ldr r7, =%(idname)s 116 swi #0 117 mov r7, ip 118 movs r0, r0 119 bxpl lr 120 b __set_syscall_errno 121""" + arm_footer 122 123arm_eabi_call_long = arm_header + """\ 124 mov ip, sp 125 .save {r4, r5, r6, r7} 126 stmfd sp!, {r4, r5, r6, r7} 127 ldmfd ip, {r4, r5, r6} 128 ldr r7, =%(idname)s 129 swi #0 130 ldmfd sp!, {r4, r5, r6, r7} 131 movs r0, r0 132 bxpl lr 133 b __set_syscall_errno 134""" + arm_footer 135 136# ARM thumb assembler templates for each syscall stub 137# 138thumb_header = """/* autogenerated by gensyscalls.py */ 139 .text 140 .type %(fname)s, #function 141 .globl %(fname)s 142 .align 4 143 .thumb_func 144 .fnstart 145 146#define __thumb__ 147#include <sys/linux-syscalls.h> 148 149 150%(fname)s: 151""" 152 153thumb_call_default = thumb_header + """\ 154 .save {r7,lr} 155 push {r7,lr} 156 ldr r7, =%(idname)s 157 swi #0 158 tst r0, r0 159 bmi 1f 160 pop {r7,pc} 1611: 162 neg r0, r0 163 ldr r1, =__set_errno 164 blx r1 165 pop {r7,pc} 166 .fnend 167""" 168 169thumb_call_long = thumb_header + """\ 170 .save {r4,r5,r7,lr} 171 push {r4,r5,r7,lr} 172 ldr r4, [sp,#16] 173 ldr r5, [sp,#20] 174 ldr r7, =%(idname)s 175 swi #0 176 tst r0, r0 177 bmi 1f 178 pop {r4,r5,r7,pc} 1791: 180 neg r0, r0 181 ldr r1, =__set_errno 182 blx r1 183 pop {r4,r5,r7,pc} 184 .fnend 185""" 186 187# mips assembler templates for each syscall stub 188# 189mips_call = """/* autogenerated by gensyscalls.py */ 190#include <sys/linux-syscalls.h> 191 .text 192 .globl %(fname)s 193 .align 4 194 .ent %(fname)s 195 196%(fname)s: 197 .set noreorder 198 .cpload $t9 199 li $v0, %(idname)s 200 syscall 201 bnez $a3, 1f 202 move $a0, $v0 203 j $ra 204 nop 2051: 206 la $t9,__set_errno 207 j $t9 208 nop 209 .set reorder 210 .end %(fname)s 211""" 212 213def param_uses_64bits(param): 214 """Returns True iff a syscall parameter description corresponds 215 to a 64-bit type.""" 216 param = param.strip() 217 # First, check that the param type begins with one of the known 218 # 64-bit types. 219 if not ( \ 220 param.startswith("int64_t") or param.startswith("uint64_t") or \ 221 param.startswith("loff_t") or param.startswith("off64_t") or \ 222 param.startswith("long long") or param.startswith("unsigned long long") or 223 param.startswith("signed long long") ): 224 return False 225 226 # Second, check that there is no pointer type here 227 if param.find("*") >= 0: 228 return False 229 230 # Ok 231 return True 232 233def count_arm_param_registers(params): 234 """This function is used to count the number of register used 235 to pass parameters when invoking a thumb or ARM system call. 236 This is because the ARM EABI mandates that 64-bit quantities 237 must be passed in an even+odd register pair. So, for example, 238 something like: 239 240 foo(int fd, off64_t pos) 241 242 would actually need 4 registers: 243 r0 -> int 244 r1 -> unused 245 r2-r3 -> pos 246 """ 247 count = 0 248 for param in params: 249 if param_uses_64bits(param): 250 if (count & 1) != 0: 251 count += 1 252 count += 2 253 else: 254 count += 1 255 return count 256 257def count_generic_param_registers(params): 258 count = 0 259 for param in params: 260 if param_uses_64bits(param): 261 count += 2 262 else: 263 count += 1 264 return count 265 266class State: 267 def __init__(self): 268 self.old_stubs = [] 269 self.new_stubs = [] 270 self.other_files = [] 271 self.syscalls = [] 272 273 def x86_genstub(self, fname, numparams, idname): 274 t = { "fname" : fname, 275 "idname" : idname } 276 277 result = x86_header % t 278 stack_bias = 4 279 for r in range(numparams): 280 result += " pushl " + x86_registers[r] + "\n" 281 stack_bias += 4 282 283 for r in range(numparams): 284 result += " mov %d(%%esp), %s" % (stack_bias+r*4, x86_registers[r]) + "\n" 285 286 result += x86_call % t 287 288 for r in range(numparams): 289 result += " popl " + x86_registers[numparams-r-1] + "\n" 290 291 result += x86_return 292 return result 293 294 def x86_genstub_cid(self, fname, numparams, idname, cid): 295 # We'll ignore numparams here because in reality, if there is a 296 # dispatch call (like a socketcall syscall) there are actually 297 # only 2 arguments to the syscall and 2 regs we have to save: 298 # %ebx <--- Argument 1 - The call id of the needed vectored 299 # syscall (socket, bind, recv, etc) 300 # %ecx <--- Argument 2 - Pointer to the rest of the arguments 301 # from the original function called (socket()) 302 t = { "fname" : fname, 303 "idname" : idname } 304 305 result = x86_header % t 306 stack_bias = 4 307 308 # save the regs we need 309 result += " pushl %ebx" + "\n" 310 stack_bias += 4 311 result += " pushl %ecx" + "\n" 312 stack_bias += 4 313 314 # set the call id (%ebx) 315 result += " mov $%d, %%ebx" % (cid) + "\n" 316 317 # set the pointer to the rest of the args into %ecx 318 result += " mov %esp, %ecx" + "\n" 319 result += " addl $%d, %%ecx" % (stack_bias) + "\n" 320 321 # now do the syscall code itself 322 result += x86_call % t 323 324 # now restore the saved regs 325 result += " popl %ecx" + "\n" 326 result += " popl %ebx" + "\n" 327 328 # epilog 329 result += x86_return 330 return result 331 332 def arm_genstub(self,fname, flags, idname): 333 t = { "fname" : fname, 334 "idname" : idname } 335 if flags: 336 numargs = int(flags) 337 if numargs > 4: 338 return arm_call_long % t 339 return arm_call_default % t 340 341 342 def arm_eabi_genstub(self,fname, flags, idname): 343 t = { "fname" : fname, 344 "idname" : idname } 345 if flags: 346 numargs = int(flags) 347 if numargs > 4: 348 return arm_eabi_call_long % t 349 return arm_eabi_call_default % t 350 351 352 def thumb_genstub(self,fname, flags, idname): 353 t = { "fname" : fname, 354 "idname" : idname } 355 if flags: 356 numargs = int(flags) 357 if numargs > 4: 358 return thumb_call_long % t 359 return thumb_call_default % t 360 361 def mips_genstub(self,fname, idname): 362 t = { "fname" : fname, 363 "idname" : idname } 364 return mips_call % t 365 366 def process_file(self,input): 367 parser = SysCallsTxtParser() 368 parser.parse_file(input) 369 self.syscalls = parser.syscalls 370 parser = None 371 372 for t in self.syscalls: 373 syscall_func = t["func"] 374 syscall_params = t["params"] 375 syscall_name = t["name"] 376 377 if t["common"] >= 0 or t["armid"] >= 0: 378 num_regs = count_arm_param_registers(syscall_params) 379 if gen_thumb_stubs: 380 t["asm-thumb"] = self.thumb_genstub(syscall_func,num_regs,"__NR_"+syscall_name) 381 else: 382 if gen_eabi_stubs: 383 t["asm-arm"] = self.arm_eabi_genstub(syscall_func,num_regs,"__NR_"+syscall_name) 384 else: 385 t["asm-arm"] = self.arm_genstub(syscall_func,num_regs,"__NR_"+syscall_name) 386 387 if t["common"] >= 0 or t["x86id"] >= 0: 388 num_regs = count_generic_param_registers(syscall_params) 389 if t["cid"] >= 0: 390 t["asm-x86"] = self.x86_genstub_cid(syscall_func, num_regs, "__NR_"+syscall_name, t["cid"]) 391 else: 392 t["asm-x86"] = self.x86_genstub(syscall_func, num_regs, "__NR_"+syscall_name) 393 elif t["cid"] >= 0: 394 E("cid for dispatch syscalls is only supported for x86 in " 395 "'%s'" % syscall_name) 396 return 397 if t["common"] >= 0 or t["mipsid"] >= 0: 398 t["asm-mips"] = self.mips_genstub(syscall_func,"__NR_"+syscall_name) 399 400 401 def gen_NR_syscall(self,fp,name,id): 402 fp.write( "#define __NR_%-25s (__NR_SYSCALL_BASE + %d)\n" % (name,id) ) 403 404 # now dump the content of linux-syscalls.h 405 def gen_linux_syscalls_h(self): 406 path = "include/sys/linux-syscalls.h" 407 D( "generating "+path ) 408 fp = create_file( path ) 409 fp.write( "/* auto-generated by gensyscalls.py, do not touch */\n" ) 410 fp.write( "#ifndef _BIONIC_LINUX_SYSCALLS_H_\n" ) 411 fp.write( "#define _BIONIC_LINUX_SYSCALLS_H_\n\n" ) 412 fp.write( "#if !defined __ASM_ARM_UNISTD_H && !defined __ASM_I386_UNISTD_H && !defined __ASM_MIPS_UNISTD_H\n" ) 413 fp.write( "#if defined __arm__ && !defined __ARM_EABI__ && !defined __thumb__\n" ) 414 fp.write( " # define __NR_SYSCALL_BASE 0x900000\n" ) 415 fp.write( "#elif defined(__mips__)\n" ) 416 fp.write( " # define __NR_SYSCALL_BASE 4000\n" ) 417 fp.write( "#else\n" ) 418 fp.write( " # define __NR_SYSCALL_BASE 0\n" ) 419 fp.write( "#endif\n\n" ) 420 421 # first, all common syscalls 422 for sc in sorted(self.syscalls,key=lambda x:x["common"]): 423 sc_id = sc["common"] 424 sc_name = sc["name"] 425 if sc_id >= 0: 426 self.gen_NR_syscall( fp, sc_name, sc_id ) 427 428 # now, all arm-specific syscalls 429 fp.write( "\n#ifdef __arm__\n" ); 430 for sc in self.syscalls: 431 sc_id = sc["armid"] 432 sc_name = sc["name"] 433 if sc_id >= 0: 434 self.gen_NR_syscall( fp, sc_name, sc_id ) 435 fp.write( "#endif\n" ); 436 437 gen_syscalls = {} 438 # finally, all i386-specific syscalls 439 fp.write( "\n#ifdef __i386__\n" ); 440 for sc in sorted(self.syscalls,key=lambda x:x["x86id"]): 441 sc_id = sc["x86id"] 442 sc_name = sc["name"] 443 if sc_id >= 0 and sc_name not in gen_syscalls: 444 self.gen_NR_syscall( fp, sc_name, sc_id ) 445 gen_syscalls[sc_name] = True 446 fp.write( "#endif\n" ); 447 448 # all mips-specific syscalls 449 fp.write( "\n#ifdef __mips__\n" ); 450 for sc in sorted(self.syscalls,key=lambda x:x["mipsid"]): 451 sc_id = sc["mipsid"] 452 if sc_id >= 0: 453 self.gen_NR_syscall( fp, sc["name"], sc_id ) 454 fp.write( "#endif\n" ); 455 456 fp.write( "\n#endif\n" ) 457 fp.write( "\n#endif /* _BIONIC_LINUX_SYSCALLS_H_ */\n" ); 458 fp.close() 459 self.other_files.append( path ) 460 461 462 # now dump the contents of syscalls.mk 463 def gen_arch_syscalls_mk(self, arch): 464 path = "arch-%s/syscalls.mk" % arch 465 D( "generating "+path ) 466 fp = create_file( path ) 467 fp.write( "# auto-generated by gensyscalls.py, do not touch\n" ) 468 fp.write( "syscall_src := \n" ) 469 arch_test = { 470 "arm": lambda x: x.has_key("asm-arm") or x.has_key("asm-thumb"), 471 "x86": lambda x: x.has_key("asm-x86"), 472 "mips": lambda x: x.has_key("asm-mips") 473 } 474 475 for sc in self.syscalls: 476 if arch_test[arch](sc): 477 fp.write("syscall_src += arch-%s/syscalls/%s.S\n" % 478 (arch, sc["func"])) 479 fp.close() 480 self.other_files.append( path ) 481 482 483 # now generate each syscall stub 484 def gen_syscall_stubs(self): 485 for sc in self.syscalls: 486 if sc.has_key("asm-arm") and 'arm' in all_archs: 487 fname = "arch-arm/syscalls/%s.S" % sc["func"] 488 D2( ">>> generating "+fname ) 489 fp = create_file( fname ) 490 fp.write(sc["asm-arm"]) 491 fp.close() 492 self.new_stubs.append( fname ) 493 494 if sc.has_key("asm-thumb") and 'arm' in all_archs: 495 fname = "arch-arm/syscalls/%s.S" % sc["func"] 496 D2( ">>> generating "+fname ) 497 fp = create_file( fname ) 498 fp.write(sc["asm-thumb"]) 499 fp.close() 500 self.new_stubs.append( fname ) 501 502 if sc.has_key("asm-x86") and 'x86' in all_archs: 503 fname = "arch-x86/syscalls/%s.S" % sc["func"] 504 D2( ">>> generating "+fname ) 505 fp = create_file( fname ) 506 fp.write(sc["asm-x86"]) 507 fp.close() 508 self.new_stubs.append( fname ) 509 510 if sc.has_key("asm-mips") and 'mips' in all_archs: 511 fname = "arch-mips/syscalls/%s.S" % sc["func"] 512 D2( ">>> generating "+fname ) 513 fp = create_file( fname ) 514 fp.write(sc["asm-mips"]) 515 fp.close() 516 self.new_stubs.append( fname ) 517 518 def regenerate(self): 519 D( "scanning for existing architecture-specific stub files" ) 520 521 bionic_root_len = len(bionic_root) 522 523 for arch in all_archs: 524 arch_path = bionic_root + "arch-" + arch 525 D( "scanning " + arch_path ) 526 files = glob.glob( arch_path + "/syscalls/*.S" ) 527 for f in files: 528 self.old_stubs.append( f[bionic_root_len:] ) 529 530 D( "found %d stub files" % len(self.old_stubs) ) 531 532 if not os.path.exists( bionic_temp ): 533 D( "creating %s" % bionic_temp ) 534 make_dir( bionic_temp ) 535 536 D( "re-generating stubs and support files" ) 537 538 self.gen_linux_syscalls_h() 539 for arch in all_archs: 540 self.gen_arch_syscalls_mk(arch) 541 self.gen_syscall_stubs() 542 543 D( "comparing files" ) 544 adds = [] 545 edits = [] 546 547 for stub in self.new_stubs + self.other_files: 548 if not os.path.exists( bionic_root + stub ): 549 # new file, git add it 550 D( "new file: " + stub) 551 adds.append( bionic_root + stub ) 552 shutil.copyfile( bionic_temp + stub, bionic_root + stub ) 553 554 elif not filecmp.cmp( bionic_temp + stub, bionic_root + stub ): 555 D( "changed file: " + stub) 556 edits.append( stub ) 557 558 deletes = [] 559 for stub in self.old_stubs: 560 if not stub in self.new_stubs: 561 D( "deleted file: " + stub) 562 deletes.append( bionic_root + stub ) 563 564 565 if adds: 566 commands.getoutput("git add " + " ".join(adds)) 567 if deletes: 568 commands.getoutput("git rm " + " ".join(deletes)) 569 if edits: 570 for file in edits: 571 shutil.copyfile( bionic_temp + file, bionic_root + file ) 572 commands.getoutput("git add " + 573 " ".join((bionic_root + file) for file in edits)) 574 575 commands.getoutput("git add %s%s" % (bionic_root,"SYSCALLS.TXT")) 576 577 if (not adds) and (not deletes) and (not edits): 578 D("no changes detected!") 579 else: 580 D("ready to go!!") 581 582D_setlevel(1) 583 584state = State() 585state.process_file(bionic_root+"SYSCALLS.TXT") 586state.regenerate() 587