static do_call(stream, node, need_lval) { auto args = node[1] - 1, i = args; while ( i ) { expr_code( stream, node[ 3 + i-- ], 0 ); asm_push( stream ); } /* If we're calling an identifier that's not local and isn't an lval, * it must either be an extern function or an extern array. The latter * would be a syntax error but for the present lack of a type system * So we assume it's a function and do a direct call if possible. */ if ( node[3][0] == 'id' ) { auto off, is_lval = lookup_sym( &node[3][3], &off ); if (!off && !is_lval) return asm_call( stream, &node[3][3], args*4 ); } /* And fall back to an indirect call with CALL *%eax. */ expr_code( stream, node[3], 0 ); call_ptr( stream, args*4 ); }
int hook_create_stub(uint8_t *tramp, const uint8_t *addr, int len) { const uint8_t *base_addr = addr; while (len > 0) { int length = lde(addr); if(length == 0) return -1; // How many bytes left? len -= length; // Unconditional jump with 32-bit relative offset. if(*addr == 0xe9) { const uint8_t *target = addr + *(int32_t *)(addr + 1) + 5; tramp += asm_jump(tramp, target); addr += 5; } // Call with 32-bit relative offset. else if(*addr == 0xe8) { const uint8_t *target = addr + *(int32_t *)(addr + 1) + 5; tramp += asm_call(tramp, target); addr += 5; } // Conditional jump with 32bit relative offset. else if(*addr == 0x0f && addr[1] >= 0x80 && addr[1] < 0x90) { #if __x86_64__ pipe("CRITICAL:Conditional jump and calls in 64-bit are " "considered unstable!"); #endif // TODO This can be stabilized by creating a 8-bit conditional // jump with 32/64-bit jumps at each target. However, this is // only required for 64-bit support and then only when this // instruction occurs at all in the original function - which is // currently not the case. // Conditional jumps consist of two bytes. *tramp++ = addr[0]; *tramp++ = addr[1]; // When a jmp/call is performed, then the relative offset + // the instruction pointer + the size of the instruction is the // resulting address, so that's our target address. // As we have already written the first one or two bytes of the // instruction we only have the relative address left - four bytes // in total. const uint8_t *target = addr + *(int32_t *)(addr + 2) + 6; // We have already copied the instruction opcode(s) itself so we // just have to calculate the relative address now. *(uint32_t *) tramp = target - tramp - 4; tramp += 4; addr += 6; } // Unconditional jump with 8bit relative offset. else if(*addr == 0xeb) { const uint8_t *target = addr + *(int8_t *)(addr + 1) + 2; tramp += asm_jump(tramp, target); addr += 2; // TODO Check the remaining length. Also keep in mind that any // following nop's behind this short jump can be included in the // remaining available space. } // Conditional jump with 8bit relative offset. else if(*addr >= 0x70 && *addr < 0x80) { #if __x86_64__ pipe("CRITICAL:Conditional jumps in 64-bit are " "considered unstable!"); #endif // TODO The same as for the 32-bit conditional jumps. // Same rules apply as with the 32bit relative offsets, except // for the fact that both conditional and unconditional 8bit // relative jumps take only one byte for the opcode. // Hex representation of the two types of 32bit jumps; // 8bit relative conditional jumps: 70..80 // 32bit relative conditional jumps: 0f 80..90 // Thus we have to add 0x10 to the opcode of 8bit relative // offset jump to obtain the 32bit relative offset jump // opcode. *tramp++ = 0x0f; *tramp++ = addr[0] + 0x10; // 8bit relative offset - we have to sign-extend it, by casting it // as signed char, in order to calculate the correct address. const uint8_t *target = addr + *(int8_t *)(addr + 1) + 2; // Calculate the relative address. *(uint32_t *) tramp = (uint32_t)(target - tramp - 4); tramp += 4; addr += 2; } #if __x86_64__ // In 64-bit mode we have RIP-relative mov and lea instructions. These // have to be relocated properly. Handles "mov reg64, qword [offset]" // and "lea reg64, qword [offset]". else if((*addr == 0x48 || *addr == 0x4c) && (addr[1] == 0x8b || addr[1] == 0x8d) && (addr[2] & 0xc7) == 0x05) { // Register index and full address. uint32_t reg = ((addr[2] >> 3) & 7) + (*addr == 0x4c ? 8 : 0); const uint8_t *target = addr + *(int32_t *)(addr + 3) + 7; // mov reg64, address tramp[0] = 0x48 + (reg >= 8); tramp[1] = 0xb8 + (reg & 7); *(const uint8_t **)(tramp + 2) = target; tramp += 10; // If it was a mov instruction then also emit the pointer // dereference part. if(addr[1] == 0x8b) { // mov reg64, qword [reg64] tramp[0] = reg < 8 ? 0x48 : 0x4d; tramp[1] = 0x8b; tramp[2] = (reg & 7) | ((reg & 7) << 3); tramp += 3; } addr += 7; } #endif // Return instruction indicates the end of basic block as well so we // have to check if we already have enough space for our hook.. else if((*addr == 0xc3 || *addr == 0xc2) && len > 0) {
// stack should look like this: // val // type static void push_expression(ClmExpNode *node) { if (node == NULL) return; ClmType expression_type = clm_type_of_exp(node, data.scope); switch (node->type) { case EXP_TYPE_INT: asm_push_const_i(node->ival); asm_push_const_i((int)expression_type); break; case EXP_TYPE_FLOAT: asm_push_const_f(node->fval); asm_push_const_i((int)expression_type); break; case EXP_TYPE_STRING: // TODO push a string onto the stack break; case EXP_TYPE_ARITH: { ClmType right_type = clm_type_of_exp(node->arithExp.right, data.scope); ClmType left_type = clm_type_of_exp(node->arithExp.left, data.scope); if (left_type == CLM_TYPE_MATRIX && clm_type_is_number(right_type)) { // here the only ops are mul & div... we are scaling matrix // gen left and then right here... if we don't then we have // int val // int type // matrix // cols // rows // matrix type // and we have to pop the int after we generate the value... which is hard // and since we are multiplying the matrix in place, it would be easiest // to // gen the matrix first and then the int, so we just have to pop two // values // in total push_expression(node->arithExp.left); asm_mov(EDX, ESP); push_expression(node->arithExp.right); gen_arith(node); } else { push_expression(node->arithExp.right); asm_mov(EDX, ESP); push_expression(node->arithExp.left); gen_arith(node); } break; } case EXP_TYPE_BOOL: push_expression(node->boolExp.right); asm_mov(EDX, ESP); push_expression(node->boolExp.left); gen_bool(node); break; case EXP_TYPE_CALL: { // first push everything thats not a matrix... and for matrices push a pointer int tempStartID = data.temporaryID; int i; ClmExpNode *param; char temporary[256]; // first for any matrices that are parameters that will be pushed through // the stack, push them on the stack and save their location into a temporary // global for (i = node->callExp.params->length - 1; i >= 0; i--) { param = node->callExp.params->data[i]; if(param->type == CLM_TYPE_MATRIX){ ClmLocation location = clm_location_of_exp(param, data.scope); switch(location){ case LOCATION_STACK: push_expression(param); next_temporary(temporary); asm_mov(temporary, ESP); break; default: break; } } } // then push every expression.. when we get to a matrix, push the pointer // to its location int tempOffset = 1; char index_str[256]; for (i = node->callExp.params->length - 1; i >= 0; i--) { param = node->callExp.params->data[i]; if(param->type == CLM_TYPE_MATRIX){ ClmLocation location = clm_location_of_exp(param, data.scope); switch(location){ case LOCATION_STACK: sprintf(temporary, "dword [temporary%d]", tempStartID + tempOffset); asm_push(temporary); tempOffset++; break; default: { // the only way its a matrix and not on the stack is if its an // ind exp with no indices ClmSymbol *symbol = clm_scope_find(data.scope, param->indExp.id); load_var_location(symbol, index_str, 0, NULL); asm_push(index_str); break; } } asm_push_const_i((int) CLM_TYPE_MATRIX); }else{ push_expression(param); } } asm_call(node->callExp.name); // TODO pop off arguments from the stack break; } case EXP_TYPE_INDEX: push_index(node); break; case EXP_TYPE_MAT_DEC: { int i; if (node->matDecExp.arr != NULL) { for (i = node->matDecExp.length - 1; i >= 0; i--) { // TODO... push f or push i? asm_push_const_i((int)node->matDecExp.arr[i]); } asm_push_const_i(node->matDecExp.size.cols); asm_push_const_i(node->matDecExp.size.rows); asm_push_const_i((int)CLM_TYPE_MATRIX); } else { // push a matrix onto the stack with all 0s char cmp_label[LABEL_SIZE]; char end_label[LABEL_SIZE]; next_label(cmp_label); next_label(end_label); gen_exp_size(node); asm_pop(EAX); // # rows asm_pop(EBX); // # cols asm_mov(ECX, EAX); asm_imul(ECX, EBX); asm_dec(ECX); asm_label(cmp_label); asm_cmp(ECX, "0"); asm_jmp_l(end_label); asm_push_const_i(0); asm_dec(ECX); asm_jmp(cmp_label); asm_label(end_label); asm_push(EBX); asm_push(EAX); asm_push_const_i((int)CLM_TYPE_MATRIX); } break; } case EXP_TYPE_PARAM: break; case EXP_TYPE_UNARY: push_expression(node->unaryExp.node); gen_unary(node); break; } }