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 process_file(self,input): 368 parser = SysCallsTxtParser() 369 parser.parse_file(input) 370 self.syscalls = parser.syscalls 371 parser = None 372 373 for t in self.syscalls: 374 syscall_func = t["func"] 375 syscall_params = t["params"] 376 syscall_name = t["name"] 377 378 if t["common"] >= 0 or t["armid"] >= 0: 379 num_regs = count_arm_param_registers(syscall_params) 380 if gen_thumb_stubs: 381 t["asm-thumb"] = self.thumb_genstub(syscall_func,num_regs,"__NR_"+syscall_name) 382 else: 383 if gen_eabi_stubs: 384 t["asm-arm"] = self.arm_eabi_genstub(syscall_func,num_regs,"__NR_"+syscall_name) 385 else: 386 t["asm-arm"] = self.arm_genstub(syscall_func,num_regs,"__NR_"+syscall_name) 387 388 if t["common"] >= 0 or t["x86id"] >= 0: 389 num_regs = count_generic_param_registers(syscall_params) 390 if t["cid"] >= 0: 391 t["asm-x86"] = self.x86_genstub_cid(syscall_func, num_regs, "__NR_"+syscall_name, t["cid"]) 392 else: 393 t["asm-x86"] = self.x86_genstub(syscall_func, num_regs, "__NR_"+syscall_name) 394 elif t["cid"] >= 0: 395 E("cid for dispatch syscalls is only supported for x86 in " 396 "'%s'" % syscall_name) 397 return 398 if t["common"] >= 0 or t["mipsid"] >= 0: 399 t["asm-mips"] = self.mips_genstub(syscall_func,"__NR_"+syscall_name) 400 401 402 def gen_NR_syscall(self,fp,name,id): 403 fp.write( "#define __NR_%-25s (__NR_SYSCALL_BASE + %d)\n" % (name,id) ) 404 405 # now dump the content of linux-syscalls.h 406 def gen_linux_syscalls_h(self): 407 path = "include/sys/linux-syscalls.h" 408 D( "generating "+path ) 409 fp = create_file( path ) 410 fp.write( "/* auto-generated by gensyscalls.py, do not touch */\n" ) 411 fp.write( "#ifndef _BIONIC_LINUX_SYSCALLS_H_\n" ) 412 fp.write( "#define _BIONIC_LINUX_SYSCALLS_H_\n\n" ) 413 fp.write( "#if !defined __ASM_ARM_UNISTD_H && !defined __ASM_I386_UNISTD_H && !defined __ASM_MIPS_UNISTD_H\n" ) 414 fp.write( "#if defined __arm__ && !defined __ARM_EABI__ && !defined __thumb__\n" ) 415 fp.write( " # define __NR_SYSCALL_BASE 0x900000\n" ) 416 fp.write( "#elif defined(__mips__)\n" ) 417 fp.write( " # define __NR_SYSCALL_BASE 4000\n" ) 418 fp.write( "#else\n" ) 419 fp.write( " # define __NR_SYSCALL_BASE 0\n" ) 420 fp.write( "#endif\n\n" ) 421 422 # first, all common syscalls 423 for sc in sorted(self.syscalls,key=lambda x:x["common"]): 424 sc_id = sc["common"] 425 sc_name = sc["name"] 426 if sc_id >= 0: 427 self.gen_NR_syscall( fp, sc_name, sc_id ) 428 429 # now, all arm-specific syscalls 430 fp.write( "\n#ifdef __arm__\n" ); 431 for sc in self.syscalls: 432 sc_id = sc["armid"] 433 sc_name = sc["name"] 434 if sc_id >= 0: 435 self.gen_NR_syscall( fp, sc_name, sc_id ) 436 fp.write( "#endif\n" ); 437 438 gen_syscalls = {} 439 # finally, all i386-specific syscalls 440 fp.write( "\n#ifdef __i386__\n" ); 441 for sc in sorted(self.syscalls,key=lambda x:x["x86id"]): 442 sc_id = sc["x86id"] 443 sc_name = sc["name"] 444 if sc_id >= 0 and sc_name not in gen_syscalls: 445 self.gen_NR_syscall( fp, sc_name, sc_id ) 446 gen_syscalls[sc_name] = True 447 fp.write( "#endif\n" ); 448 449 # all mips-specific syscalls 450 fp.write( "\n#ifdef __mips__\n" ); 451 for sc in sorted(self.syscalls,key=lambda x:x["mipsid"]): 452 sc_id = sc["mipsid"] 453 if sc_id >= 0: 454 self.gen_NR_syscall( fp, sc["name"], sc_id ) 455 fp.write( "#endif\n" ); 456 457 fp.write( "\n#endif\n" ) 458 fp.write( "\n#endif /* _BIONIC_LINUX_SYSCALLS_H_ */\n" ); 459 fp.close() 460 self.other_files.append( path ) 461 462 463 # now dump the contents of syscalls.mk 464 def gen_arch_syscalls_mk(self, arch): 465 path = "arch-%s/syscalls.mk" % arch 466 D( "generating "+path ) 467 fp = create_file( path ) 468 fp.write( "# auto-generated by gensyscalls.py, do not touch\n" ) 469 fp.write( "syscall_src := \n" ) 470 arch_test = { 471 "arm": lambda x: x.has_key("asm-arm") or x.has_key("asm-thumb"), 472 "x86": lambda x: x.has_key("asm-x86"), 473 "mips": lambda x: x.has_key("asm-mips") 474 } 475 476 for sc in self.syscalls: 477 if arch_test[arch](sc): 478 fp.write("syscall_src += arch-%s/syscalls/%s.S\n" % 479 (arch, sc["func"])) 480 fp.close() 481 self.other_files.append( path ) 482 483 484 # now generate each syscall stub 485 def gen_syscall_stubs(self): 486 for sc in self.syscalls: 487 if sc.has_key("asm-arm") and 'arm' in all_archs: 488 fname = "arch-arm/syscalls/%s.S" % sc["func"] 489 D2( ">>> generating "+fname ) 490 fp = create_file( fname ) 491 fp.write(sc["asm-arm"]) 492 fp.close() 493 self.new_stubs.append( fname ) 494 495 if sc.has_key("asm-thumb") and 'arm' in all_archs: 496 fname = "arch-arm/syscalls/%s.S" % sc["func"] 497 D2( ">>> generating "+fname ) 498 fp = create_file( fname ) 499 fp.write(sc["asm-thumb"]) 500 fp.close() 501 self.new_stubs.append( fname ) 502 503 if sc.has_key("asm-x86") and 'x86' in all_archs: 504 fname = "arch-x86/syscalls/%s.S" % sc["func"] 505 D2( ">>> generating "+fname ) 506 fp = create_file( fname ) 507 fp.write(sc["asm-x86"]) 508 fp.close() 509 self.new_stubs.append( fname ) 510 511 if sc.has_key("asm-mips") and 'mips' in all_archs: 512 fname = "arch-mips/syscalls/%s.S" % sc["func"] 513 D2( ">>> generating "+fname ) 514 fp = create_file( fname ) 515 fp.write(sc["asm-mips"]) 516 fp.close() 517 self.new_stubs.append( fname ) 518 519 def regenerate(self): 520 D( "scanning for existing architecture-specific stub files" ) 521 522 bionic_root_len = len(bionic_root) 523 524 for arch in all_archs: 525 arch_path = bionic_root + "arch-" + arch 526 D( "scanning " + arch_path ) 527 files = glob.glob( arch_path + "/syscalls/*.S" ) 528 for f in files: 529 self.old_stubs.append( f[bionic_root_len:] ) 530 531 D( "found %d stub files" % len(self.old_stubs) ) 532 533 if not os.path.exists( bionic_temp ): 534 D( "creating %s" % bionic_temp ) 535 make_dir( bionic_temp ) 536 537# D( "p4 editing source files" ) 538# for arch in all_archs: 539# commands.getoutput( "p4 edit " + arch + "/syscalls/*.S " ) 540# commands.getoutput( "p4 edit " + arch + "/syscalls.mk" ) 541# commands.getoutput( "p4 edit " + bionic_root + "include/sys/linux-syscalls.h" ) 542 543 D( "re-generating stubs and support files" ) 544 545 self.gen_linux_syscalls_h() 546 for arch in all_archs: 547 self.gen_arch_syscalls_mk(arch) 548 self.gen_syscall_stubs() 549 550 D( "comparing files" ) 551 adds = [] 552 edits = [] 553 554 for stub in self.new_stubs + self.other_files: 555 if not os.path.exists( bionic_root + stub ): 556 # new file, git add it 557 D( "new file: " + stub) 558 adds.append( bionic_root + stub ) 559 shutil.copyfile( bionic_temp + stub, bionic_root + stub ) 560 561 elif not filecmp.cmp( bionic_temp + stub, bionic_root + stub ): 562 D( "changed file: " + stub) 563 edits.append( stub ) 564 565 deletes = [] 566 for stub in self.old_stubs: 567 if not stub in self.new_stubs: 568 D( "deleted file: " + stub) 569 deletes.append( bionic_root + stub ) 570 571 572 if adds: 573 commands.getoutput("git add " + " ".join(adds)) 574 if deletes: 575 commands.getoutput("git rm " + " ".join(deletes)) 576 if edits: 577 for file in edits: 578 shutil.copyfile( bionic_temp + file, bionic_root + file ) 579 commands.getoutput("git add " + 580 " ".join((bionic_root + file) for file in edits)) 581 582 commands.getoutput("git add %s%s" % (bionic_root,"SYSCALLS.TXT")) 583 584 if (not adds) and (not deletes) and (not edits): 585 D("no changes detected!") 586 else: 587 D("ready to go!!") 588 589D_setlevel(1) 590 591state = State() 592state.process_file(bionic_root+"SYSCALLS.TXT") 593state.regenerate() 594