gensyscalls.py revision 4e468ed2eb86a2406e14f1eca82072ee501d05fd
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" ] 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 <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 93arm_call_default = arm_header + """\ 94 swi #%(idname)s 95 movs r0, r0 96 bxpl lr 97 b __set_syscall_errno 98 .fnend 99""" 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 .fnend 112""" 113 114arm_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 126arm_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# 142thumb_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 157thumb_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} 1651: 166 neg r0, r0 167 ldr r1, =__set_errno 168 blx r1 169 pop {r7,pc} 170 .fnend 171""" 172 173thumb_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} 1831: 184 neg r0, r0 185 ldr r1, =__set_errno 186 blx r1 187 pop {r4,r5,r7,pc} 188 .fnend 189""" 190 191 192class State: 193 def __init__(self): 194 self.old_stubs = [] 195 self.new_stubs = [] 196 self.other_files = [] 197 self.syscalls = [] 198 199 def x86_genstub(self, fname, numparams, idname): 200 t = { "fname" : fname, 201 "idname" : idname } 202 203 result = x86_header % t 204 stack_bias = 4 205 for r in range(numparams): 206 result += " pushl " + x86_registers[r] + "\n" 207 stack_bias += 4 208 209 for r in range(numparams): 210 result += " mov %d(%%esp), %s" % (stack_bias+r*4, x86_registers[r]) + "\n" 211 212 result += x86_call % t 213 214 for r in range(numparams): 215 result += " popl " + x86_registers[numparams-r-1] + "\n" 216 217 result += x86_return 218 return result 219 220 def x86_genstub_cid(self, fname, numparams, idname, cid): 221 # We'll ignore numparams here because in reality, if there is a 222 # dispatch call (like a socketcall syscall) there are actually 223 # only 2 arguments to the syscall and 2 regs we have to save: 224 # %ebx <--- Argument 1 - The call id of the needed vectored 225 # syscall (socket, bind, recv, etc) 226 # %ecx <--- Argument 2 - Pointer to the rest of the arguments 227 # from the original function called (socket()) 228 t = { "fname" : fname, 229 "idname" : idname } 230 231 result = x86_header % t 232 stack_bias = 4 233 234 # save the regs we need 235 result += " pushl %ebx" + "\n" 236 stack_bias += 4 237 result += " pushl %ecx" + "\n" 238 stack_bias += 4 239 240 # set the call id (%ebx) 241 result += " mov $%d, %%ebx" % (cid) + "\n" 242 243 # set the pointer to the rest of the args into %ecx 244 result += " mov %esp, %ecx" + "\n" 245 result += " addl $%d, %%ecx" % (stack_bias) + "\n" 246 247 # now do the syscall code itself 248 result += x86_call % t 249 250 # now restore the saved regs 251 result += " popl %ecx" + "\n" 252 result += " popl %ebx" + "\n" 253 254 # epilog 255 result += x86_return 256 return result 257 258 def arm_genstub(self,fname, flags, idname): 259 t = { "fname" : fname, 260 "idname" : idname } 261 if flags: 262 numargs = int(flags) 263 if numargs > 4: 264 return arm_call_long % t 265 return arm_call_default % t 266 267 268 def arm_eabi_genstub(self,fname, flags, idname): 269 t = { "fname" : fname, 270 "idname" : idname } 271 if flags: 272 numargs = int(flags) 273 if numargs > 4: 274 return arm_eabi_call_long % t 275 return arm_eabi_call_default % t 276 277 278 def thumb_genstub(self,fname, flags, idname): 279 t = { "fname" : fname, 280 "idname" : idname } 281 if flags: 282 numargs = int(flags) 283 if numargs > 4: 284 return thumb_call_long % t 285 return thumb_call_default % t 286 287 288 def process_file(self,input): 289 parser = SysCallsTxtParser() 290 parser.parse_file(input) 291 self.syscalls = parser.syscalls 292 parser = None 293 294 for t in self.syscalls: 295 syscall_func = t["func"] 296 syscall_params = t["params"] 297 syscall_name = t["name"] 298 299 if t["id"] >= 0: 300 if gen_thumb_stubs: 301 t["asm-thumb"] = self.thumb_genstub(syscall_func,len(syscall_params),"__NR_"+syscall_name) 302 else: 303 if gen_eabi_stubs: 304 t["asm-arm"] = self.arm_eabi_genstub(syscall_func,len(syscall_params),"__NR_"+syscall_name) 305 else: 306 t["asm-arm"] = self.arm_genstub(syscall_func,len(syscall_params),"__NR_"+syscall_name) 307 308 if t["id2"] >= 0: 309 if t["cid"] >= 0: 310 t["asm-x86"] = self.x86_genstub_cid(syscall_func, len(syscall_params), "__NR_"+syscall_name, t["cid"]) 311 else: 312 t["asm-x86"] = self.x86_genstub(syscall_func,len(syscall_params),"__NR_"+syscall_name) 313 elif t["cid"] >= 0: 314 E("cid for dispatch syscalls is only supported for x86 in " 315 "'%s'" % syscall_name) 316 return 317 318 319 def gen_NR_syscall(self,fp,name,id): 320 fp.write( "#define __NR_%-25s (__NR_SYSCALL_BASE + %d)\n" % (name,id) ) 321 322 # now dump the content of linux/_syscalls.h 323 def gen_linux_syscalls_h(self): 324 path = "include/sys/linux-syscalls.h" 325 D( "generating "+path ) 326 fp = create_file( path ) 327 fp.write( "/* auto-generated by gensyscalls.py, do not touch */\n" ) 328 fp.write( "#ifndef _BIONIC_LINUX_SYSCALLS_H_\n\n" ) 329 fp.write( "#if !defined __ASM_ARM_UNISTD_H && !defined __ASM_I386_UNISTD_H\n" ) 330 fp.write( "#if defined __arm__ && !defined __ARM_EABI__ && !defined __thumb__\n" ) 331 fp.write( " # define __NR_SYSCALL_BASE 0x900000\n" ) 332 fp.write( " #else\n" ) 333 fp.write( " # define __NR_SYSCALL_BASE 0\n" ) 334 fp.write( " #endif\n\n" ) 335 336 # first, all common syscalls 337 for sc in self.syscalls: 338 sc_id = sc["id"] 339 sc_id2 = sc["id2"] 340 sc_name = sc["name"] 341 if sc_id == sc_id2 and sc_id >= 0: 342 self.gen_NR_syscall( fp, sc_name, sc_id ) 343 344 # now, all arm-specific syscalls 345 fp.write( "\n#ifdef __arm__\n" ); 346 for sc in self.syscalls: 347 sc_id = sc["id"] 348 sc_id2 = sc["id2"] 349 sc_name = sc["name"] 350 if sc_id != sc_id2 and sc_id >= 0: 351 self.gen_NR_syscall( fp, sc_name, sc_id ) 352 fp.write( "#endif\n" ); 353 354 gen_syscalls = {} 355 # finally, all i386-specific syscalls 356 fp.write( "\n#ifdef __i386__\n" ); 357 for sc in self.syscalls: 358 sc_id = sc["id"] 359 sc_id2 = sc["id2"] 360 sc_name = sc["name"] 361 if sc_id != sc_id2 and sc_id2 >= 0 and sc_name not in gen_syscalls: 362 self.gen_NR_syscall( fp, sc_name, sc_id2 ) 363 gen_syscalls[sc_name] = True 364 fp.write( "#endif\n" ); 365 366 fp.write( "\n#endif\n" ) 367 fp.write( "\n#endif /* _BIONIC_LINUX_SYSCALLS_H_ */\n" ); 368 fp.close() 369 self.other_files.append( path ) 370 371 372 # now dump the content of linux/_syscalls.h 373 def gen_linux_unistd_h(self): 374 path = "include/sys/linux-unistd.h" 375 D( "generating "+path ) 376 fp = create_file( path ) 377 fp.write( "/* auto-generated by gensyscalls.py, do not touch */\n" ) 378 fp.write( "#ifndef _BIONIC_LINUX_UNISTD_H_\n\n" ); 379 fp.write( "#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n" ) 380 381 for sc in self.syscalls: 382 fp.write( sc["decl"]+"\n" ) 383 384 fp.write( "#ifdef __cplusplus\n}\n#endif\n" ) 385 fp.write( "\n#endif /* _BIONIC_LINUX_UNISTD_H_ */\n" ); 386 fp.close() 387 self.other_files.append( path ) 388 389 # now dump the contents of syscalls.mk 390 def gen_arch_syscalls_mk(self, arch): 391 path = "arch-%s/syscalls.mk" % arch 392 D( "generating "+path ) 393 fp = create_file( path ) 394 fp.write( "# auto-generated by gensyscalls.py, do not touch\n" ) 395 fp.write( "syscall_src := \n" ) 396 arch_test = { 397 "arm": lambda x: x.has_key("asm-arm") or x.has_key("asm-thumb"), 398 "x86": lambda x: x.has_key("asm-x86") 399 } 400 401 for sc in self.syscalls: 402 if arch_test[arch](sc): 403 fp.write("syscall_src += arch-%s/syscalls/%s.S\n" % 404 (arch, sc["func"])) 405 fp.close() 406 self.other_files.append( path ) 407 408 # now generate each syscall stub 409 def gen_syscall_stubs(self): 410 for sc in self.syscalls: 411 if sc.has_key("asm-arm") and 'arm' in all_archs: 412 fname = "arch-arm/syscalls/%s.S" % sc["func"] 413 D( ">>> generating "+fname ) 414 fp = create_file( fname ) 415 fp.write(sc["asm-arm"]) 416 fp.close() 417 self.new_stubs.append( fname ) 418 419 if sc.has_key("asm-thumb") and 'arm' in all_archs: 420 fname = "arch-arm/syscalls/%s.S" % sc["func"] 421 D( ">>> generating "+fname ) 422 fp = create_file( fname ) 423 fp.write(sc["asm-thumb"]) 424 fp.close() 425 self.new_stubs.append( fname ) 426 427 if sc.has_key("asm-x86") and 'x86' in all_archs: 428 fname = "arch-x86/syscalls/%s.S" % sc["func"] 429 D( ">>> generating "+fname ) 430 fp = create_file( fname ) 431 fp.write(sc["asm-x86"]) 432 fp.close() 433 self.new_stubs.append( fname ) 434 435 436 def regenerate(self): 437 D( "scanning for existing architecture-specific stub files" ) 438 439 bionic_root_len = len(bionic_root) 440 441 for arch in all_archs: 442 arch_path = bionic_root + "arch-" + arch 443 D( "scanning " + arch_path ) 444 files = glob.glob( arch_path + "/syscalls/*.S" ) 445 for f in files: 446 self.old_stubs.append( f[bionic_root_len:] ) 447 448 D( "found %d stub files" % len(self.old_stubs) ) 449 450 if not os.path.exists( bionic_temp ): 451 D( "creating %s" % bionic_temp ) 452 os.mkdir( bionic_temp ) 453 454# D( "p4 editing source files" ) 455# for arch in all_archs: 456# commands.getoutput( "p4 edit " + arch + "/syscalls/*.S " ) 457# commands.getoutput( "p4 edit " + arch + "/syscalls.mk" ) 458# commands.getoutput( "p4 edit " + bionic_root + "include/sys/linux-syscalls.h" ) 459 460 D( "re-generating stubs and support files" ) 461 462 self.gen_linux_syscalls_h() 463 for arch in all_archs: 464 self.gen_arch_syscalls_mk(arch) 465 self.gen_linux_unistd_h() 466 self.gen_syscall_stubs() 467 468 D( "comparing files" ) 469 adds = [] 470 edits = [] 471 472 for stub in self.new_stubs + self.other_files: 473 if not os.path.exists( bionic_root + stub ): 474 # new file, P4 add it 475 D( "new file: " + stub) 476 adds.append( bionic_root + stub ) 477 shutil.copyfile( bionic_temp + stub, bionic_root + stub ) 478 479 elif not filecmp.cmp( bionic_temp + stub, bionic_root + stub ): 480 D( "changed file: " + stub) 481 edits.append( stub ) 482 483 deletes = [] 484 for stub in self.old_stubs: 485 if not stub in self.new_stubs: 486 D( "deleted file: " + stub) 487 deletes.append( bionic_root + stub ) 488 489 490 if adds: 491 commands.getoutput("p4 add " + " ".join(adds)) 492 if deletes: 493 commands.getoutput("p4 delete " + " ".join(deletes)) 494 if edits: 495 commands.getoutput("p4 edit " + 496 " ".join((bionic_root + file) for file in edits)) 497 for file in edits: 498 shutil.copyfile( bionic_temp + file, bionic_root + file ) 499 500 D("ready to go !!") 501 502D_setlevel(1) 503 504state = State() 505state.process_file(bionic_root+"SYSCALLS.TXT") 506state.regenerate() 507