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 8 import sys, os.path, glob, re, commands, filecmp, shutil 9 10 from bionic_utils import * 11 12 # set this to 1 if you want to generate thumb stubs 13 gen_thumb_stubs = 0 14 15 # set this to 1 if you want to generate ARM EABI stubs 16 gen_eabi_stubs = 1 17 18 # get the root Bionic directory, simply this script's dirname 19 # 20 bionic_root = find_bionic_root() 21 if not bionic_root: 22 print "could not find the Bionic root directory. aborting" 23 sys.exit(1) 24 25 if bionic_root[-1] != '/': 26 bionic_root += "/" 27 28 print "bionic_root is %s" % bionic_root 29 30 # temp directory where we store all intermediate files 31 bionic_temp = "/tmp/bionic_gensyscalls/" 32 33 # all architectures, update as you see fit 34 all_archs = [ "arm", "x86", "sh" ] 35 36 def 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 43 def 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 51 x86_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 62 x86_registers = [ "%ebx", "%ecx", "%edx", "%esi", "%edi", "%ebp" ] 63 64 x86_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 73 1: 74 """ 75 76 x86_return = """ ret 77 """ 78 79 # ARM assembler templates for each syscall stub 80 # 81 arm_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 93 arm_call_default = arm_header + """\ 94 swi #%(idname)s 95 movs r0, r0 96 bxpl lr 97 b __set_syscall_errno 98 .fnend 99 """ 100 101 arm_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 114 arm_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 126 arm_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 # 142 thumb_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 157 thumb_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} 165 1: 166 neg r0, r0 167 ldr r1, =__set_errno 168 blx r1 169 pop {r7,pc} 170 .fnend 171 """ 172 173 thumb_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} 183 1: 184 neg r0, r0 185 ldr r1, =__set_errno 186 blx r1 187 pop {r4,r5,r7,pc} 188 .fnend 189 """ 190 191 # SuperH assembler templates for each syscall stub 192 # 193 superh_header = """/* autogenerated by gensyscalls.py */ 194 #include <sys/linux-syscalls.h> 195 196 .text 197 .type %(fname)s, @function 198 .globl %(fname)s 199 .align 4 200 201 %(fname)s: 202 """ 203 204 superh_call_default = """ 205 /* invoke trap */ 206 mov.l 0f, r3 /* trap num */ 207 trapa #(%(numargs)s + 0x10) 208 209 /* check return value */ 210 cmp/pz r0 211 bt %(idname)s_end 212 213 /* keep error number */ 214 sts.l pr, @-r15 215 mov.l 1f, r1 216 jsr @r1 217 mov r0, r4 218 lds.l @r15+, pr 219 220 %(idname)s_end: 221 rts 222 nop 223 224 .align 2 225 0: .long %(idname)s 226 1: .long __set_syscall_errno 227 """ 228 229 superh_5args_header = """ 230 /* get ready for additonal arg */ 231 mov.l @r15, r0 232 """ 233 234 superh_6args_header = """ 235 /* get ready for additonal arg */ 236 mov.l @r15, r0 237 mov.l @(4, r15), r1 238 """ 239 240 superh_7args_header = """ 241 /* get ready for additonal arg */ 242 mov.l @r15, r0 243 mov.l @(4, r15), r1 244 mov.l @(8, r15), r2 245 """ 246 247 248 class State: 249 def __init__(self): 250 self.old_stubs = [] 251 self.new_stubs = [] 252 self.other_files = [] 253 self.syscalls = [] 254 255 def x86_genstub(self, fname, numparams, idname): 256 t = { "fname" : fname, 257 "idname" : idname } 258 259 result = x86_header % t 260 stack_bias = 4 261 for r in range(numparams): 262 result += " pushl " + x86_registers[r] + "\n" 263 stack_bias += 4 264 265 for r in range(numparams): 266 result += " mov %d(%%esp), %s" % (stack_bias+r*4, x86_registers[r]) + "\n" 267 268 result += x86_call % t 269 270 for r in range(numparams): 271 result += " popl " + x86_registers[numparams-r-1] + "\n" 272 273 result += x86_return 274 return result 275 276 def x86_genstub_cid(self, fname, numparams, idname, cid): 277 # We'll ignore numparams here because in reality, if there is a 278 # dispatch call (like a socketcall syscall) there are actually 279 # only 2 arguments to the syscall and 2 regs we have to save: 280 # %ebx <--- Argument 1 - The call id of the needed vectored 281 # syscall (socket, bind, recv, etc) 282 # %ecx <--- Argument 2 - Pointer to the rest of the arguments 283 # from the original function called (socket()) 284 t = { "fname" : fname, 285 "idname" : idname } 286 287 result = x86_header % t 288 stack_bias = 4 289 290 # save the regs we need 291 result += " pushl %ebx" + "\n" 292 stack_bias += 4 293 result += " pushl %ecx" + "\n" 294 stack_bias += 4 295 296 # set the call id (%ebx) 297 result += " mov $%d, %%ebx" % (cid) + "\n" 298 299 # set the pointer to the rest of the args into %ecx 300 result += " mov %esp, %ecx" + "\n" 301 result += " addl $%d, %%ecx" % (stack_bias) + "\n" 302 303 # now do the syscall code itself 304 result += x86_call % t 305 306 # now restore the saved regs 307 result += " popl %ecx" + "\n" 308 result += " popl %ebx" + "\n" 309 310 # epilog 311 result += x86_return 312 return result 313 314 def arm_genstub(self,fname, flags, idname): 315 t = { "fname" : fname, 316 "idname" : idname } 317 if flags: 318 numargs = int(flags) 319 if numargs > 4: 320 return arm_call_long % t 321 return arm_call_default % t 322 323 324 def arm_eabi_genstub(self,fname, flags, idname): 325 t = { "fname" : fname, 326 "idname" : idname } 327 if flags: 328 numargs = int(flags) 329 if numargs > 4: 330 return arm_eabi_call_long % t 331 return arm_eabi_call_default % t 332 333 334 def thumb_genstub(self,fname, flags, idname): 335 t = { "fname" : fname, 336 "idname" : idname } 337 if flags: 338 numargs = int(flags) 339 if numargs > 4: 340 return thumb_call_long % t 341 return thumb_call_default % t 342 343 344 def superh_genstub(self, fname, flags, idname): 345 numargs = int(flags) 346 t = { "fname" : fname, 347 "idname" : idname, 348 "numargs" : numargs } 349 superh_call = superh_header 350 if flags: 351 if numargs == 5: 352 superh_call += superh_5args_header 353 if numargs == 6: 354 superh_call += superh_6args_header 355 if numargs == 7: 356 superh_call += superh_7args_header 357 superh_call += superh_call_default 358 return superh_call % t 359 360 361 def process_file(self,input): 362 parser = SysCallsTxtParser() 363 parser.parse_file(input) 364 self.syscalls = parser.syscalls 365 parser = None 366 367 for t in self.syscalls: 368 syscall_func = t["func"] 369 syscall_params = t["params"] 370 syscall_name = t["name"] 371 372 if t["id"] >= 0: 373 if gen_thumb_stubs: 374 t["asm-thumb"] = self.thumb_genstub(syscall_func,len(syscall_params),"__NR_"+syscall_name) 375 else: 376 if gen_eabi_stubs: 377 t["asm-arm"] = self.arm_eabi_genstub(syscall_func,len(syscall_params),"__NR_"+syscall_name) 378 else: 379 t["asm-arm"] = self.arm_genstub(syscall_func,len(syscall_params),"__NR_"+syscall_name) 380 381 if t["id2"] >= 0: 382 if t["cid"] >= 0: 383 t["asm-x86"] = self.x86_genstub_cid(syscall_func, len(syscall_params), "__NR_"+syscall_name, t["cid"]) 384 else: 385 t["asm-x86"] = self.x86_genstub(syscall_func,len(syscall_params),"__NR_"+syscall_name) 386 elif t["cid"] >= 0: 387 E("cid for dispatch syscalls is only supported for x86 in " 388 "'%s'" % syscall_name) 389 return 390 if t["id3"] >= 0: 391 t["asm-sh"] = self.superh_genstub(syscall_func,len(syscall_params),"__NR_"+syscall_name) 392 393 394 395 def gen_NR_syscall(self,fp,name,id): 396 fp.write( "#define __NR_%-25s (__NR_SYSCALL_BASE + %d)\n" % (name,id) ) 397 398 # now dump the content of linux/_syscalls.h 399 def gen_linux_syscalls_h(self): 400 path = "include/sys/linux-syscalls.h" 401 D( "generating "+path ) 402 fp = create_file( path ) 403 fp.write( "/* auto-generated by gensyscalls.py, do not touch */\n" ) 404 fp.write( "#ifndef _BIONIC_LINUX_SYSCALLS_H_\n\n" ) 405 fp.write( "#if !defined __ASM_ARM_UNISTD_H && !defined __ASM_I386_UNISTD_H\n" ) 406 fp.write( "#if defined __arm__ && !defined __ARM_EABI__ && !defined __thumb__\n" ) 407 fp.write( " # define __NR_SYSCALL_BASE 0x900000\n" ) 408 fp.write( " #else\n" ) 409 fp.write( " # define __NR_SYSCALL_BASE 0\n" ) 410 fp.write( " #endif\n\n" ) 411 412 # first, all common syscalls 413 for sc in self.syscalls: 414 sc_id = sc["id"] 415 sc_id2 = sc["id2"] 416 sc_name = sc["name"] 417 if sc_id == sc_id2 and sc_id >= 0: 418 self.gen_NR_syscall( fp, sc_name, sc_id ) 419 420 # now, all arm-specific syscalls 421 fp.write( "\n#ifdef __arm__\n" ); 422 for sc in self.syscalls: 423 sc_id = sc["id"] 424 sc_id2 = sc["id2"] 425 sc_name = sc["name"] 426 if sc_id != sc_id2 and sc_id >= 0: 427 self.gen_NR_syscall( fp, sc_name, sc_id ) 428 fp.write( "#endif\n" ); 429 430 gen_syscalls = {} 431 # finally, all i386-specific syscalls 432 fp.write( "\n#ifdef __i386__\n" ); 433 for sc in self.syscalls: 434 sc_id = sc["id"] 435 sc_id2 = sc["id2"] 436 sc_name = sc["name"] 437 if sc_id != sc_id2 and sc_id2 >= 0 and sc_name not in gen_syscalls: 438 self.gen_NR_syscall( fp, sc_name, sc_id2 ) 439 gen_syscalls[sc_name] = True 440 fp.write( "#endif\n" ); 441 442 # all superh-specific syscalls 443 fp.write( "\n#if defined(__SH3__) || defined(__SH4__) \n" ); 444 for sc in self.syscalls: 445 sc_id = sc["id"] 446 sc_id2 = sc["id2"] 447 sc_id3 = sc["id3"] 448 sc_name = sc["name"] 449 if sc_id2 != sc_id3 and sc_id3 >= 0: 450 self.gen_NR_syscall( fp, sc_name, sc_id3 ) 451 else: 452 if sc_id != sc_id2 and sc_id2 >= 0: 453 self.gen_NR_syscall( fp, sc_name, sc_id2 ) 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 content of linux/_syscalls.h 463 def gen_linux_unistd_h(self): 464 path = "include/sys/linux-unistd.h" 465 D( "generating "+path ) 466 fp = create_file( path ) 467 fp.write( "/* auto-generated by gensyscalls.py, do not touch */\n" ) 468 fp.write( "#ifndef _BIONIC_LINUX_UNISTD_H_\n\n" ); 469 fp.write( "#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n" ) 470 471 for sc in self.syscalls: 472 fp.write( sc["decl"]+"\n" ) 473 474 fp.write( "#ifdef __cplusplus\n}\n#endif\n" ) 475 fp.write( "\n#endif /* _BIONIC_LINUX_UNISTD_H_ */\n" ); 476 fp.close() 477 self.other_files.append( path ) 478 479 # now dump the contents of syscalls.mk 480 def gen_arch_syscalls_mk(self, arch): 481 path = "arch-%s/syscalls.mk" % arch 482 D( "generating "+path ) 483 fp = create_file( path ) 484 fp.write( "# auto-generated by gensyscalls.py, do not touch\n" ) 485 fp.write( "syscall_src := \n" ) 486 arch_test = { 487 "arm": lambda x: x.has_key("asm-arm") or x.has_key("asm-thumb"), 488 "x86": lambda x: x.has_key("asm-x86"), 489 "sh": lambda x: x.has_key("asm-sh") 490 } 491 492 for sc in self.syscalls: 493 if arch_test[arch](sc): 494 fp.write("syscall_src += arch-%s/syscalls/%s.S\n" % 495 (arch, sc["func"])) 496 fp.close() 497 self.other_files.append( path ) 498 499 # now generate each syscall stub 500 def gen_syscall_stubs(self): 501 for sc in self.syscalls: 502 if sc.has_key("asm-arm") and 'arm' in all_archs: 503 fname = "arch-arm/syscalls/%s.S" % sc["func"] 504 D( ">>> generating "+fname ) 505 fp = create_file( fname ) 506 fp.write(sc["asm-arm"]) 507 fp.close() 508 self.new_stubs.append( fname ) 509 510 if sc.has_key("asm-thumb") and 'arm' in all_archs: 511 fname = "arch-arm/syscalls/%s.S" % sc["func"] 512 D( ">>> generating "+fname ) 513 fp = create_file( fname ) 514 fp.write(sc["asm-thumb"]) 515 fp.close() 516 self.new_stubs.append( fname ) 517 518 if sc.has_key("asm-x86") and 'x86' in all_archs: 519 fname = "arch-x86/syscalls/%s.S" % sc["func"] 520 D( ">>> generating "+fname ) 521 fp = create_file( fname ) 522 fp.write(sc["asm-x86"]) 523 fp.close() 524 self.new_stubs.append( fname ) 525 526 if sc.has_key("asm-sh"): 527 fname = "arch-sh/syscalls/%s.S" % sc["func"] 528 D( ">>> generating "+fname ) 529 fp = create_file( fname ) 530 fp.write(sc["asm-sh"]) 531 fp.close() 532 self.new_stubs.append( fname ) 533 534 535 def regenerate(self): 536 D( "scanning for existing architecture-specific stub files" ) 537 538 bionic_root_len = len(bionic_root) 539 540 for arch in all_archs: 541 arch_path = bionic_root + "arch-" + arch 542 D( "scanning " + arch_path ) 543 files = glob.glob( arch_path + "/syscalls/*.S" ) 544 for f in files: 545 self.old_stubs.append( f[bionic_root_len:] ) 546 547 D( "found %d stub files" % len(self.old_stubs) ) 548 549 if not os.path.exists( bionic_temp ): 550 D( "creating %s" % bionic_temp ) 551 os.mkdir( bionic_temp ) 552 553 # D( "p4 editing source files" ) 554 # for arch in all_archs: 555 # commands.getoutput( "p4 edit " + arch + "/syscalls/*.S " ) 556 # commands.getoutput( "p4 edit " + arch + "/syscalls.mk" ) 557 # commands.getoutput( "p4 edit " + bionic_root + "include/sys/linux-syscalls.h" ) 558 559 D( "re-generating stubs and support files" ) 560 561 self.gen_linux_syscalls_h() 562 for arch in all_archs: 563 self.gen_arch_syscalls_mk(arch) 564 self.gen_linux_unistd_h() 565 self.gen_syscall_stubs() 566 567 D( "comparing files" ) 568 adds = [] 569 edits = [] 570 571 for stub in self.new_stubs + self.other_files: 572 if not os.path.exists( bionic_root + stub ): 573 # new file, P4 add it 574 D( "new file: " + stub) 575 adds.append( bionic_root + stub ) 576 shutil.copyfile( bionic_temp + stub, bionic_root + stub ) 577 578 elif not filecmp.cmp( bionic_temp + stub, bionic_root + stub ): 579 D( "changed file: " + stub) 580 edits.append( stub ) 581 582 deletes = [] 583 for stub in self.old_stubs: 584 if not stub in self.new_stubs: 585 D( "deleted file: " + stub) 586 deletes.append( bionic_root + stub ) 587 588 589 if adds: 590 commands.getoutput("p4 add " + " ".join(adds)) 591 if deletes: 592 commands.getoutput("p4 delete " + " ".join(deletes)) 593 if edits: 594 commands.getoutput("p4 edit " + 595 " ".join((bionic_root + file) for file in edits)) 596 for file in edits: 597 shutil.copyfile( bionic_temp + file, bionic_root + file ) 598 599 D("ready to go !!") 600 601 D_setlevel(1) 602 603 state = State() 604 state.process_file(bionic_root+"SYSCALLS.TXT") 605 state.regenerate() 606