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 patch_instruction(self, first_offset): if isinstance(self.instruction, asm.MOV): # Moving an address inside a register, we need to change the address here # If the MOVE + JUMP is supposed to go just after, put NOP instead diff = first_offset - self.position if diff > 0 and diff <= 13: nop = asm.NOP().encode() for i in range(diff): jitcompiler_instance.global_allocator.write_instruction( nop, self.position) self.position += 1 else: # It's a real jump, just compile it and patch the code new_address = lib.get_address( ffi.from_buffer( jitcompiler_instance.global_allocator.code_section), first_offset) new_instruction = asm.MOV(self.instruction.operands[0], new_address) # Create the new encoded instruction and replace the old one in the code section encoded = new_instruction.encode() jitcompiler_instance.global_allocator.write_instruction( encoded, self.position) elif isinstance(self.instruction, asm.JG): new_operand = first_offset - self.position - len( self.instruction.encode()) # Update to the new position new_instruction = asm.JG( asm.operand.RIPRelativeOffset(new_operand)) encoded = new_instruction.encode() # If the previous instruction was a 32 bits offset, force it to the new one if len(self.instruction.encode()) > 2: encoded = bytearray(len(self.instruction.encode())) # Force the 32 encoding of the JG instruction encoded[0] = 0x0F encoded[1] = 0x8F encoded[2] = 0 encoded[3] = 0 encoded[4] = 0 encoded[5] = 0 size = custom_ceil(new_operand / 255) bytes = new_operand.to_bytes(size, 'little') for i in range(0, len(bytes)): encoded[i + 2] = bytes[i] jitcompiler_instance.global_allocator.write_instruction( encoded, self.position) elif isinstance(self.instruction, asm.JGE): new_operand = first_offset - self.position - len( self.instruction.encode()) # Update to the new position new_instruction = asm.JGE( asm.operand.RIPRelativeOffset(new_operand)) encoded = new_instruction.encode() # If the previous instruction was a 32 bits offset, force it to the new one if len(encoded) != 6 and len(self.instruction.encode()) > 2: encoded = bytearray(6) # Force the 32 encoding of the JGE encoded[0] = 0x0F encoded[1] = 0x8D encoded[2] = 0 encoded[3] = 0 encoded[4] = 0 encoded[5] = 0 # Keep the same value for the jump size = custom_ceil(new_operand / 255) bytes = new_operand.to_bytes(size, 'little') for i in range(0, len(bytes)): encoded[i + 2] = bytes[i] jitcompiler_instance.global_allocator.write_instruction( encoded, self.position) elif isinstance(self.instruction, asm.JE): new_operand = first_offset - self.position - len( self.instruction.encode()) # Update to the new position new_instruction = asm.JE( asm.operand.RIPRelativeOffset(new_operand)) encoded = new_instruction.encode() # If the previous instruction was a 32 bits offset, force it to the new one if len(self.instruction.encode()) > 2: encoded = bytearray(len(self.instruction.encode())) # Force the 32 encoding of the JE instruction encoded[0] = 0x0F encoded[1] = 0x84 encoded[2] = 0 encoded[3] = 0 encoded[4] = 0 encoded[5] = 0 size = custom_ceil(new_operand / 255) bytes = new_operand.to_bytes(size, 'little') for i in range(0, len(bytes)): encoded[i + 2] = bytes[i] jitcompiler_instance.global_allocator.write_instruction( encoded, self.position) elif isinstance(self.instruction, asm.JL): new_operand = first_offset - self.position - len( self.instruction.encode()) new_instruction = asm.JL( asm.operand.RIPRelativeOffset(new_operand)) encoded = new_instruction.encode() # If the previous instruction was a 32 bits offset, force the same length for the new one if len(self.instruction.encode()) > 2 and len(encoded) != 6: encoded = bytearray(len(self.instruction.encode())) # Force the 32 encoding of the JL instruction encoded[0] = 0x0F encoded[1] = 0x8C encoded[2] = 0 encoded[3] = 0 encoded[4] = 0 encoded[5] = 0 size = custom_ceil(new_operand / 255) bytes = None if size < 0 or new_operand < 0: size = 4 bytes = new_operand.to_bytes(size, 'little', signed=True) else: bytes = new_operand.to_bytes(size, 'little') for i in range(0, len(bytes)): encoded[i + 2] = bytes[i] jitcompiler_instance.global_allocator.write_instruction( encoded, self.position) elif isinstance(self.instruction, asm.JLE): new_operand = first_offset - self.position - len( self.instruction.encode()) # Update to the new position new_instruction = asm.JLE( asm.operand.RIPRelativeOffset(new_operand)) encoded = new_instruction.encode() # If the previous instruction was a 32 bits offset, force it to the new one if len(encoded) != 6 and len(self.instruction.encode()) > 2: encoded = bytearray(6) # Force the 32 encoding of the JGE encoded[0] = 0x0F encoded[1] = 0x8E encoded[2] = 0 encoded[3] = 0 encoded[4] = 0 encoded[5] = 0 # Keep the same value for the jump size = custom_ceil(new_operand / 255) bytes = new_operand.to_bytes(size, 'little') for i in range(0, len(bytes)): encoded[i + 2] = bytes[i] jitcompiler_instance.global_allocator.write_instruction( encoded, self.position) elif isinstance(self.instruction, asm.JNE): new_operand = first_offset - self.position - len( self.instruction.encode()) if new_operand < 255: # We will encode the instruction new_operand = first_offset - self.position - 2 new_instruction = asm.JNE( asm.operand.RIPRelativeOffset(new_operand)) encoded = new_instruction.encode() # Need to add some NOP instruction to fill the space left from the previous longer instruction if len(encoded) < len(self.instruction.encode()): diff = len(self.instruction.encode()) - len(encoded) for i in range(diff): encoded += asm.NOP().encode() else: # If the previous instruction was a 32 bits offset, force the same length for the new one encoded = bytearray(len(self.instruction.encode())) # Force the 32 encoding of the JNE instruction encoded[0] = 0x0F encoded[1] = 0x85 encoded[2] = 0 encoded[3] = 0 encoded[4] = 0 encoded[5] = 0 size = custom_ceil(new_operand / 255) bytes = new_operand.to_bytes(size, 'little') for i in range(0, len(bytes)): encoded[i + 2] = bytes[i] jitcompiler_instance.global_allocator.write_instruction( encoded, self.position) elif isinstance(self.instruction, asm.JB): old_instruction_encoded = self.instruction.encode() new_operand = first_offset - self.position - len( old_instruction_encoded) if new_operand < 255: # We will encode the instruction new_operand = first_offset - self.position - len( old_instruction_encoded) new_instruction = asm.JB( asm.operand.RIPRelativeOffset(new_operand)) encoded = new_instruction.encode() # Need to add some NOP instruction to fill the space left from the previous longer instruction if len(old_instruction_encoded) != len(new_instruction.encode()): # If the previous instruction was a 32 bits offset, force the same length for the new one encoded = bytearray(len(old_instruction_encoded)) # Force the 32 encoding of the JNE instruction encoded[0] = 0x0F encoded[1] = 0x82 encoded[2] = 0 encoded[3] = 0 encoded[4] = 0 encoded[5] = 0 size = custom_ceil(new_operand / 255) bytes = new_operand.to_bytes(size, 'little') for i in range(0, len(bytes)): encoded[i + 2] = bytes[i] jitcompiler_instance.global_allocator.write_instruction( encoded, self.position) else: print("Not yet implemented patch " + str(self.instruction))