blob: dca820a335c8c5939b685e2443eb1bf2855f7c96 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012 Wind River Systems, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
* You may elect to redistribute this code under either of these licenses.
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
/*
* This module implements preparation of a new stack before calling a function.
* This code is a stub. Downstream code will provide real implementation.
*/
#include <tcf/config.h>
#if ENABLE_Symbols && ENABLE_DebugContext
#include <assert.h>
#include <tcf/framework/myalloc.h>
#include <tcf/framework/cpudefs.h>
#include <tcf/services/funccall.h>
#define EM_386 3 /* Intel Architecture */
#define EM_X86_64 62 /* AMD x86-64 architecture */
static FunctionCallInfo * info = NULL;
static unsigned trace_cmds_max = 0;
static unsigned trace_cmds_cnt = 0;
static LocationExpressionCommand * trace_cmds = NULL;
static Symbol * func_type = NULL;
static Symbol * func_return_type = NULL;
static Symbol ** func_args = NULL;
static int func_args_cnt = 0;
static ContextAddress func_return_size = 0;
static int func_return_type_class = 0;
static ContextAddress * arg_size_formal = NULL;
static int * arg_class_formal = NULL;
static int * arg_class_actual = NULL;
static int reg_arg_ids[] = { 5, 4, 1, 2, 8, 9 };
static LocationExpressionCommand * add_command(int op) {
LocationExpressionCommand * cmd = NULL;
if (trace_cmds_cnt >= trace_cmds_max) {
trace_cmds_max += 16;
trace_cmds = (LocationExpressionCommand *)tmp_realloc(trace_cmds, trace_cmds_max * sizeof(LocationExpressionCommand));
}
cmd = trace_cmds + trace_cmds_cnt++;
memset(cmd, 0, sizeof(*cmd));
cmd->cmd = op;
if (op == SFT_CMD_RD_MEM || op == SFT_CMD_WR_MEM) {
cmd->args.mem.big_endian = info->scope.big_endian;
}
return cmd;
}
#if 0 /* Not used */
static LocationExpressionCommand * add_command_location(uint8_t * code, size_t code_size) {
LocationExpressionCommand * cmd = NULL;
cmd = add_command(SFT_CMD_LOCATION);
cmd->args.loc.code_addr = code;
cmd->args.loc.code_size = code_size;
cmd->args.loc.reg_id_scope = info->scope;
cmd->args.loc.addr_size = info->scope.elf64? 64 : 32;
return cmd;
}
#endif
static int get_stack_pointer_register_id(void) {
switch (info->scope.machine) {
case EM_386: return 4;
case EM_X86_64: return 7;
}
return -1;
}
static int get_return_value_register_id(void) {
switch (info->scope.machine) {
case EM_386: return 0;
case EM_X86_64: return 0;
}
return -1;
}
static RegisterDefinition * find_register(int id) {
if (id < 0) return NULL;
return get_reg_by_id(info->ctx, id, &info->scope);
}
static int c_call_cmds(void) {
unsigned i;
unsigned sp_offs = 0;
int * reg_arg = (int *)tmp_alloc_zero(sizeof(int) * info->args_cnt);
RegisterDefinition * reg_rv = find_register(get_return_value_register_id());
RegisterDefinition * reg_sp = find_register(get_stack_pointer_register_id());
RegisterDefinition * reg_pc = get_PC_definition(info->ctx);
if (reg_sp == NULL) {
set_errno(ERR_OTHER, "Don't know stack pointer register");
return -1;
}
if (reg_pc == NULL) {
set_errno(ERR_OTHER, "Don't know instruction pointer register");
return -1;
}
if (reg_rv == NULL) {
set_errno(ERR_OTHER, "Don't know function return value register");
return -1;
}
info->stak_pointer = reg_sp;
if (info->scope.machine == EM_386) {
info->stack_alignment = 8;
}
else if (info->scope.machine == EM_X86_64) {
unsigned reg_args_cnt = 0;
info->stack_alignment = 16;
info->red_zone_size = 128;
/* Assign arguments to registers */
for (i = 0; i < info->args_cnt; i++) {
unsigned arg_no = i;
switch (arg_class_actual[arg_no]) {
case TYPE_CLASS_CARDINAL:
case TYPE_CLASS_INTEGER:
case TYPE_CLASS_POINTER:
case TYPE_CLASS_ENUMERATION:
case TYPE_CLASS_ARRAY:
switch (arg_class_formal[arg_no]) {
case TYPE_CLASS_CARDINAL:
case TYPE_CLASS_INTEGER:
case TYPE_CLASS_POINTER:
case TYPE_CLASS_ENUMERATION:
case TYPE_CLASS_ARRAY:
if (reg_args_cnt < sizeof(reg_arg_ids) / sizeof(int)) {
add_command(SFT_CMD_ARG)->args.arg_no = FUNCCALL_ARG_ARGS + arg_no;
add_command(SFT_CMD_WR_REG)->args.reg = find_register(reg_arg_ids[reg_args_cnt++]);
reg_arg[i] = 1;
}
break;
}
break;
}
}
}
/* Push arguments to call stack */
for (i = 0; i < info->args_cnt; i++) {
unsigned arg_no = info->args_cnt - i - 1;
if (reg_arg[arg_no]) continue;
switch (arg_class_actual[arg_no]) {
case TYPE_CLASS_CARDINAL:
case TYPE_CLASS_INTEGER:
case TYPE_CLASS_POINTER:
case TYPE_CLASS_ENUMERATION:
case TYPE_CLASS_ARRAY:
switch (arg_class_formal[arg_no]) {
case TYPE_CLASS_CARDINAL:
case TYPE_CLASS_INTEGER:
case TYPE_CLASS_POINTER:
case TYPE_CLASS_ENUMERATION:
case TYPE_CLASS_ARRAY:
sp_offs += (unsigned)arg_size_formal[arg_no];
while (sp_offs % (info->scope.elf64 ? 8 : 4) != 0) sp_offs++;
add_command(SFT_CMD_RD_REG)->args.reg = reg_sp;
add_command(SFT_CMD_NUMBER)->args.num = sp_offs;
add_command(SFT_CMD_SUB);
add_command(SFT_CMD_ARG)->args.arg_no = FUNCCALL_ARG_ARGS + arg_no;
add_command(SFT_CMD_WR_MEM)->args.mem.size = (size_t)arg_size_formal[arg_no];
break;
default:
set_errno(ERR_OTHER, "Unsupported argument type");
return -1;
}
break;
default:
set_errno(ERR_OTHER, "Unsupported argument type");
return -1;
}
}
/* Push current PC to the stack as return address */
sp_offs += reg_pc->size;
add_command(SFT_CMD_RD_REG)->args.reg = reg_sp;
add_command(SFT_CMD_NUMBER)->args.num = sp_offs;
add_command(SFT_CMD_SUB);
add_command(SFT_CMD_RD_REG)->args.reg = reg_pc;
add_command(SFT_CMD_WR_MEM)->args.mem.size = reg_pc->size;
/* Update stack pointer register */
add_command(SFT_CMD_RD_REG)->args.reg = reg_sp;
add_command(SFT_CMD_NUMBER)->args.num = sp_offs;
add_command(SFT_CMD_SUB);
add_command(SFT_CMD_WR_REG)->args.reg = reg_sp;
/* Execute the call */
add_command(SFT_CMD_FCALL);
/* Get function return value */
if (func_return_size == 0) return 0;
switch (func_return_type_class) {
case TYPE_CLASS_CARDINAL:
case TYPE_CLASS_INTEGER:
case TYPE_CLASS_POINTER:
case TYPE_CLASS_ENUMERATION:
case TYPE_CLASS_ARRAY:
add_command(SFT_CMD_RD_REG)->args.reg = reg_rv;
break;
default:
set_errno(ERR_OTHER, "Unsupported return type");
return -1;
}
return 0;
}
static void save_registers(void) {
unsigned cnt = 0;
RegisterDefinition * r;
RegisterDefinition * regs = get_reg_definitions(info->ctx);
for (r = regs; r->name != NULL; r++) {
if (r->dwarf_id < 0) continue;
if (r->size == 0) continue;
cnt++;
}
info->saveregs = (RegisterDefinition **)tmp_alloc(sizeof(RegisterDefinition *) * cnt);
for (r = regs; r->name != NULL; r++) {
if (r->dwarf_id < 0) continue;
if (r->size == 0) continue;
info->saveregs[info->saveregs_cnt++] = r;
}
assert(info->saveregs_cnt == cnt);
}
int get_function_call_location_expression(FunctionCallInfo * arg_info) {
unsigned i;
int sym_class = SYM_CLASS_UNKNOWN;
info = arg_info;
trace_cmds_cnt = 0;
trace_cmds_max = 0;
trace_cmds = NULL;
if (get_symbol_class(info->func, &sym_class) < 0) return -1;
if (sym_class == SYM_CLASS_FUNCTION) {
if (get_symbol_type(info->func, &func_type) < 0) return -1;
}
else if (sym_class == SYM_CLASS_TYPE) {
func_type = (Symbol *)info->func;
}
else {
set_errno(ERR_OTHER, "Invalid function symbol");
return -1;
}
if (get_symbol_children(func_type, &func_args, &func_args_cnt) < 0) return -1;
if (get_symbol_base_type(func_type, &func_return_type) < 0) return -1;
if (get_symbol_size(func_return_type, &func_return_size) < 0) return -1;
if (get_symbol_type_class(func_return_type, &func_return_type_class) < 0) return -1;
arg_class_formal = (int *)tmp_alloc(sizeof(int) * info->args_cnt);
arg_class_actual = (int *)tmp_alloc(sizeof(int) * info->args_cnt);
arg_size_formal = (ContextAddress *)tmp_alloc(sizeof(ContextAddress) * info->args_cnt);
for (i = 0; i < info->args_cnt; i++) {
const Symbol * s = info->args[i];
arg_class_formal[i] = TYPE_CLASS_INTEGER;
arg_class_actual[i] = TYPE_CLASS_INTEGER;
arg_size_formal[i] = info->scope.elf64 ? 8 : 4;
/* If argument type is not given, assume 'int' */
if (s != NULL && get_symbol_type_class(s, arg_class_actual + i) < 0) return -1;
if (i < (unsigned)func_args_cnt) {
if (get_symbol_type_class(func_args[i], arg_class_formal + i) < 0) return -1;
if (get_symbol_size(func_args[i], arg_size_formal + i) < 0) return -1;
}
}
switch (info->scope.os_abi) {
case 0:
if (c_call_cmds() < 0) return -1;
break;
default:
set_errno(ERR_OTHER, "Unsupported ABI code");
return -1;
}
if (trace_cmds_cnt > 0) {
save_registers();
info->cmds = trace_cmds;
info->cmds_cnt = trace_cmds_cnt;
return 0;
}
set_errno(ERR_OTHER, "Calling functions is not supported");
return -1;
}
#endif /* ENABLE_Symbols && ENABLE_DebugContext */