def compile_function_stub(self, mfunction, nbargs, address_after): # Now encode the stub stub_label = asm.Label("Stub_label_" + str(mfunction.name)) # The call to that will be compiled after the stub compilation is over address = mfunction.allocator.encode_stub(asm.LABEL(stub_label)) # Save the association mfunction.allocator.jump_labels[address] = asm.LABEL(stub_label) # Save the rsp for the stub mfunction.allocator.encode_stub(asm.MOV(asm.rdi, asm.registers.rsp)) # Call the stub function in C function_address = int( ffi.cast("intptr_t", ffi.addressof(lib, "function_stub"))) # Align the stack on 16 bits mfunction.allocator.encode_stub(asm.MOV(asm.rax, asm.registers.rsp)) mfunction.allocator.encode_stub(asm.AND(asm.registers.rsp, -16)) mfunction.allocator.encode_stub(asm.PUSH(asm.registers.rsp)) mfunction.allocator.encode_stub(asm.MOV(asm.r10, function_address)) mfunction.allocator.encode_stub(asm.CALL(asm.r10)) # Now put additional informations for the stub # Force min 8 bits encoding for this value nbargs_bytes = encode_bytes(nbargs) stub_function = StubFunction() self.stub_dictionary[address_after] = stub_function self.data_addresses[ address_after] = jitcompiler_instance.global_allocator.stub_offset address_after_bytes = address_after.to_bytes( address_after.bit_length(), "little") # Write after the stub jitcompiler_instance.global_allocator.stub_offset = jitcompiler_instance.global_allocator.write_instruction( nbargs_bytes, jitcompiler_instance.global_allocator.stub_offset) jitcompiler_instance.global_allocator.stub_offset = jitcompiler_instance.global_allocator.write_instruction( address_after_bytes, jitcompiler_instance.global_allocator.stub_offset) for i in range(5): mfunction.allocator.encode_stub(asm.NOP()) return address
def encode_stub_test(self, branch, label, type_value): # Giving rsp to C function address = self.mfunction.allocator.encode_stub( asm.MOV(asm.rdi, asm.registers.rsp)) # Save the association self.mfunction.allocator.jump_labels[address] = label # Call to C to compile the block reg_id = asm.r10 function_address = int( ffi.cast("intptr_t", ffi.addressof(lib, "type_test_stub"))) # Align the stack on 16 bits self.mfunction.allocator.encode_stub( asm.MOV(asm.rax, asm.registers.rsp)) self.mfunction.allocator.encode_stub(asm.PUSH(asm.registers.rsp)) self.mfunction.allocator.encode_stub(asm.MOV(reg_id, function_address)) self.mfunction.allocator.encode_stub(asm.CALL(reg_id)) # Compute the return address to link this stub to self return_address = lib.get_address( ffi.from_buffer( jitcompiler_instance.global_allocator.code_section), jitcompiler_instance.global_allocator.stub_offset) return_address = return_address & -16 # Associate this return address to self in the stub_handler stubhandler_instance.stub_dictionary[return_address] = self variable_id = encode_bytes(self.variable) type_bytes = encode_bytes(type_value) offset = jitcompiler_instance.global_allocator.stub_offset stubhandler_instance.data_addresses[return_address] = offset jitcompiler_instance.global_allocator.stub_offset = jitcompiler_instance.global_allocator.write_instruction( variable_id, offset) jitcompiler_instance.global_allocator.stub_offset = jitcompiler_instance.global_allocator.write_instruction( type_bytes, jitcompiler_instance.global_allocator.stub_offset) # Saving some space for the cleaning for i in range(5): self.mfunction.allocator.encode_stub(asm.NOP()) return return_address
def compile_class_stub(self, mfunction): # Push on the stack the address of the class' stub c_buffer = ffi.from_buffer( jitcompiler_instance.global_allocator.code_section) address_stub = lib.get_address( c_buffer, jitcompiler_instance.global_allocator.stub_offset) mfunction.allocator.encode(asm.MOV(asm.r10, address_stub)) mfunction.allocator.encode(asm.PUSH(asm.r10)) # Save the address after the PUSH to be able to jump there later c_buffer = ffi.from_buffer( jitcompiler_instance.global_allocator.code_section) address_code = lib.get_address( c_buffer, jitcompiler_instance.global_allocator.code_offset) # Be able to find them in the collection during the later callback self.class_stub_addresses.append(address_stub) # Call the stub function mfunction.allocator.encode_stub(asm.MOV(asm.rdi, asm.registers.rsp)) function_address = int( ffi.cast("intptr_t", ffi.addressof(lib, "class_stub"))) mfunction.allocator.encode_stub(asm.MOV(asm.r10, function_address)) mfunction.allocator.encode_stub(asm.CALL(asm.r10)) offset = jitcompiler_instance.global_allocator.stub_offset return_address = lib.get_address( ffi.from_buffer( jitcompiler_instance.global_allocator.code_section), jitcompiler_instance.global_allocator.stub_offset) stub = StubClass() stub.data_address = offset stub.return_address = address_code # Indicate this offset correspond to the "return address" on the stub after the call to C returned self.data_addresses[return_address] = offset self.stub_dictionary[return_address] = stub # Reserve some space for the cleanup for i in range(5): mfunction.allocator.encode_stub(asm.NOP())
def compile_stub(self, mfunction, stub): # The call to that will be compiled after the stub compilation is over stub_label = "Stub_label_" + str(id(stub)) # Now we store the stack pointer to patch it later address = mfunction.allocator.encode_stub( asm.MOV(asm.rdi, asm.registers.rsp)) # Save the association mfunction.allocator.jump_labels[address] = stub_label reg_id = asm.r10 function_address = int( ffi.cast("intptr_t", ffi.addressof(lib, "bb_stub"))) # Align the stack on 16 bits mfunction.allocator.encode_stub(asm.MOV(asm.rax, asm.registers.rsp)) mfunction.allocator.encode_stub(asm.PUSH(asm.registers.rsp)) mfunction.allocator.encode_stub(asm.MOV(reg_id, function_address)) mfunction.allocator.encode_stub(asm.CALL(reg_id)) # Save the offset offset = jitcompiler_instance.global_allocator.stub_offset return_address = lib.get_address( ffi.from_buffer( jitcompiler_instance.global_allocator.code_section), jitcompiler_instance.global_allocator.stub_offset) # Indicate this offset correspond to the "return address" on the stub after the call to C returned self.data_addresses[return_address] = offset self.stub_dictionary[return_address] = stub # Save some space for cleaning instructions for i in range(15): mfunction.allocator.encode_stub(asm.NOP()) return address
def clean(self, class_address, address_class_function): instructions = [] # Now push the function address instructions.append(asm.MOV(asm.rax, class_address)) instructions.append(asm.PUSH(asm.rax)) instructions.append(asm.MOV(asm.r10, address_class_function)) instructions.append(asm.CALL(asm.r10)) # Clean the stack instructions.append(asm.ADD(asm.registers.rsp, 32)) # Finally, jump to the correct destination instructions.append(asm.MOV(asm.rax, class_address)) instructions.append(asm.MOV(asm.r11, self.return_address)) instructions.append(asm.JMP(asm.r11)) offset = self.data_address for i in instructions: offset = jitcompiler_instance.global_allocator.write_instruction( i.encode(), offset)
def clean(self, return_address, function_address, canary_value=None): instructions = [] # restore rsp instructions.append(asm.POP(asm.registers.rsp).encode()) if canary_value in stubhandler_instance.class_stub_addresses: instructions.append(asm.ADD(asm.registers.rsp, 32).encode()) else: # Discard the three top values on the stack instructions.append(asm.ADD(asm.registers.rsp, 24).encode()) # Now push the function address instructions.append(asm.MOV(asm.rax, function_address).encode()) instructions.append(asm.PUSH(asm.rax).encode()) # Finally, jump to the correct destination instructions.append(asm.MOV(asm.rax, return_address).encode()) instructions.append(asm.JMP(asm.rax).encode()) offset = self.data_address for i in instructions: offset = jitcompiler_instance.global_allocator.write_instruction( i, offset)
def allocate_instance(self, class_address, init_function, class_function): # Save the address of the initializer definition c_buffer = stub_handler.ffi.from_buffer(self.global_allocator.code_section) init_code_address = stub_handler.lib.get_address(c_buffer, self.global_allocator.code_offset) self.global_allocator.jitcompiler.initializer_addresses[class_function.name] = init_code_address instructions = list() # Move the next available address into rax to return it instructions.append(asm.MOV(asm.rax, self.register_allocation)) # Construct the header with the size of the object size = 2 instructions.append(asm.MOV(asm.operand.MemoryOperand(asm.r15), size)) # Put a pointer to the class address in the second field instructions.append(asm.MOV(asm.r10, class_address)) instructions.append(asm.MOV(asm.operand.MemoryOperand(self.register_allocation + 8), asm.r10)) # Increment the allocation pointer instructions.append(asm.ADD(self.register_allocation, 8 * 5)) # Finally, tag the address inside rax tag_instructions = self.global_allocator.jitcompiler.tags.tag_object_asm(asm.rax) instructions.extend(tag_instructions) # Now call the __init__() method of the class if any # TODO: handle __init__ with more than 1 parameters if init_function is not None: init_offset = 4 # Save the return address of the current call instructions.append(asm.POP(asm.r9)) # Saving parameter instructions.append(asm.POP(asm.r8)) # Depop the class address instructions.append(asm.ADD(asm.registers.rsp, 8)) # TODO: problem stack size instructions.append(asm.PUSH(asm.r9)) # Push back object and parameters instructions.append(asm.PUSH(asm.rax)) instructions.append(asm.PUSH(asm.r8)) # Make the call to init instructions.append(asm.ADD(asm.r10, 8 * init_offset)) instructions.append(asm.CALL(asm.operand.MemoryOperand(asm.r10))) # Saving return address in a register instructions.append(asm.POP(asm.r9)) instructions.append(asm.JMP(asm.r9)) offset = self.global_allocator.code_offset for i in instructions: offset = self.global_allocator.write_instruction(i.encode(), offset) self.global_allocator.code_offset = offset