2003-05-20 15:58:07 +08:00
|
|
|
/* dw2gencfi.c - Support for generating Dwarf2 CFI information.
|
|
|
|
Copyright 2003 Free Software Foundation, Inc.
|
|
|
|
Contributed by Michal Ludvig <mludvig@suse.cz>
|
|
|
|
|
|
|
|
This file is part of GAS, the GNU Assembler.
|
|
|
|
|
|
|
|
GAS is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
|
|
any later version.
|
|
|
|
|
|
|
|
GAS is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with GAS; see the file COPYING. If not, write to the Free
|
|
|
|
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
|
|
|
02111-1307, USA. */
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include "as.h"
|
|
|
|
#include "dw2gencfi.h"
|
|
|
|
|
|
|
|
/* Current target config. */
|
|
|
|
static struct cfi_config current_config;
|
|
|
|
|
|
|
|
/* This is the main entry point to the CFI machinery. */
|
|
|
|
static void dot_cfi (int arg);
|
|
|
|
|
|
|
|
const pseudo_typeS cfi_pseudo_table[] =
|
|
|
|
{
|
|
|
|
{ "cfi_verbose", dot_cfi, CFI_verbose },
|
|
|
|
{ "cfi_startproc", dot_cfi, CFI_startproc },
|
|
|
|
{ "cfi_endproc", dot_cfi, CFI_endproc },
|
|
|
|
{ "cfi_def_cfa", dot_cfi, CFA_def_cfa },
|
|
|
|
{ "cfi_def_cfa_register", dot_cfi, CFA_def_cfa_register },
|
|
|
|
{ "cfi_def_cfa_offset", dot_cfi, CFA_def_cfa_offset },
|
|
|
|
{ "cfi_adjust_cfa_offset", dot_cfi, CFI_adjust_cfa_offset },
|
|
|
|
{ "cfi_offset", dot_cfi, CFA_offset },
|
|
|
|
{ NULL, NULL, 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
cfi_insn_str (enum cfi_insn insn)
|
|
|
|
{
|
|
|
|
switch (insn)
|
|
|
|
{
|
|
|
|
case CFA_nop:
|
|
|
|
return "CFA_nop";
|
|
|
|
case CFA_set_loc:
|
|
|
|
return "CFA_set_loc";
|
|
|
|
case CFA_advance_loc1:
|
|
|
|
return "CFA_advance_loc1";
|
|
|
|
case CFA_advance_loc2:
|
|
|
|
return "CFA_advance_loc2";
|
|
|
|
case CFA_advance_loc4:
|
|
|
|
return "CFA_advance_loc4";
|
|
|
|
case CFA_offset_extended:
|
|
|
|
return "CFA_offset_extended";
|
|
|
|
case CFA_resotre_extended:
|
|
|
|
return "CFA_resotre_extended";
|
|
|
|
case CFA_undefined:
|
|
|
|
return "CFA_undefined";
|
|
|
|
case CFA_same_value:
|
|
|
|
return "CFA_same_value";
|
|
|
|
case CFA_register:
|
|
|
|
return "CFA_register";
|
|
|
|
case CFA_remember_state:
|
|
|
|
return "CFA_remember_state";
|
|
|
|
case CFA_restore_state:
|
|
|
|
return "CFA_restore_state";
|
|
|
|
case CFA_def_cfa:
|
|
|
|
return "CFA_def_cfa";
|
|
|
|
case CFA_def_cfa_register:
|
|
|
|
return "CFA_def_cfa_register";
|
|
|
|
case CFA_def_cfa_offset:
|
|
|
|
return "CFA_def_cfa_offset";
|
|
|
|
case CFA_advance_loc:
|
|
|
|
return "CFA_advance_loc";
|
|
|
|
case CFA_offset:
|
|
|
|
return "CFA_offset";
|
|
|
|
case CFA_restore:
|
|
|
|
return "CFA_restore";
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return "CFA_unknown";
|
|
|
|
}
|
|
|
|
|
|
|
|
struct cfi_data
|
|
|
|
{
|
|
|
|
enum cfi_insn insn;
|
|
|
|
long param[2];
|
|
|
|
struct cfi_data *next;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct cfi_info
|
|
|
|
{
|
|
|
|
addressT start_address;
|
|
|
|
addressT end_address;
|
|
|
|
addressT last_address;
|
|
|
|
const char *labelname;
|
|
|
|
struct cfi_data *data;
|
|
|
|
struct cfi_info *next;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct cfi_info *cfi_info;
|
|
|
|
|
|
|
|
static struct cfi_data *
|
|
|
|
alloc_cfi_data (void)
|
|
|
|
{
|
|
|
|
return (struct cfi_data *) xcalloc (sizeof (struct cfi_info), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cfi_info *
|
|
|
|
alloc_cfi_info (void)
|
|
|
|
{
|
|
|
|
return (struct cfi_info *) xcalloc (sizeof (struct cfi_info), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse arguments. */
|
|
|
|
static int
|
|
|
|
cfi_parse_arg (long *param, int resolvereg)
|
|
|
|
{
|
|
|
|
long value;
|
|
|
|
int retval = -1;
|
|
|
|
int nchars;
|
|
|
|
|
|
|
|
assert (param != NULL);
|
|
|
|
SKIP_WHITESPACE ();
|
|
|
|
|
|
|
|
if (sscanf (input_line_pointer, "%li%n", &value, &nchars) >= 1)
|
|
|
|
{
|
|
|
|
input_line_pointer += nchars;
|
|
|
|
retval = 1;
|
|
|
|
}
|
2003-05-20 19:35:45 +08:00
|
|
|
#ifdef tc_regname_to_dw2regnum
|
2003-05-20 15:58:07 +08:00
|
|
|
else if (resolvereg && (is_name_beginner (*input_line_pointer)))
|
|
|
|
{
|
2003-05-20 19:35:45 +08:00
|
|
|
char *name, c, *p;
|
|
|
|
|
2003-05-20 15:58:07 +08:00
|
|
|
name = input_line_pointer;
|
|
|
|
c = get_symbol_end ();
|
|
|
|
p = input_line_pointer;
|
|
|
|
|
|
|
|
if ((value = tc_regname_to_dw2regnum (name)) >= 0)
|
|
|
|
retval = 1;
|
|
|
|
|
|
|
|
*p = c;
|
|
|
|
}
|
2003-05-20 19:35:45 +08:00
|
|
|
#endif
|
2003-05-20 15:58:07 +08:00
|
|
|
else
|
|
|
|
as_bad (resolvereg ?
|
|
|
|
_("can't convert argument to a register number") :
|
|
|
|
_("can't convert argument to an integer"));
|
|
|
|
|
|
|
|
if (retval > 0)
|
|
|
|
*param = value;
|
|
|
|
|
|
|
|
SKIP_WHITESPACE ();
|
|
|
|
if (*input_line_pointer == ',')
|
|
|
|
{
|
|
|
|
input_line_pointer++;
|
|
|
|
SKIP_WHITESPACE ();
|
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
cfi_parse_reg (long *param)
|
|
|
|
{
|
|
|
|
return cfi_parse_arg (param, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
cfi_parse_const (long *param)
|
|
|
|
{
|
|
|
|
return cfi_parse_arg (param, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cfi_add_insn (enum cfi_insn insn, long param0, long param1)
|
|
|
|
{
|
|
|
|
struct cfi_data *data_ptr;
|
|
|
|
|
|
|
|
if (!cfi_info->data)
|
|
|
|
{
|
|
|
|
cfi_info->data = alloc_cfi_data ();
|
|
|
|
data_ptr = cfi_info->data;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
data_ptr = cfi_info->data;
|
|
|
|
|
|
|
|
while (data_ptr && data_ptr->next)
|
|
|
|
data_ptr = data_ptr->next;
|
|
|
|
|
|
|
|
data_ptr->next = alloc_cfi_data ();
|
|
|
|
|
|
|
|
data_ptr = data_ptr->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
data_ptr->insn = insn;
|
|
|
|
data_ptr->param[0] = param0;
|
|
|
|
data_ptr->param[1] = param1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
cfi_advance_loc (void)
|
|
|
|
{
|
|
|
|
addressT curr_address = frag_now_fix ();
|
|
|
|
if (cfi_info->last_address == curr_address)
|
|
|
|
return;
|
|
|
|
cfi_add_insn (CFA_advance_loc,
|
|
|
|
(long) (curr_address - cfi_info->last_address), 0);
|
|
|
|
cfi_info->last_address = curr_address;
|
|
|
|
}
|
|
|
|
|
|
|
|
static long
|
|
|
|
get_current_offset (struct cfi_info *info)
|
|
|
|
{
|
|
|
|
long current_offset = 0;
|
|
|
|
struct cfi_data *data = info->data;
|
|
|
|
|
|
|
|
current_offset = 0;
|
|
|
|
while (data)
|
|
|
|
{
|
|
|
|
if (data->insn == CFA_def_cfa)
|
|
|
|
current_offset = data->param[1];
|
|
|
|
else if (data->insn == CFA_def_cfa_offset)
|
|
|
|
current_offset = data->param[0];
|
|
|
|
data = data->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return current_offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
cfi_make_insn (int arg)
|
|
|
|
{
|
|
|
|
long param[2] = { 0, 0 };
|
|
|
|
|
|
|
|
if (!cfi_info)
|
|
|
|
{
|
|
|
|
as_bad (_("CFI instruction used without previous .cfi_startproc"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
cfi_advance_loc ();
|
|
|
|
|
|
|
|
switch (arg)
|
|
|
|
{
|
|
|
|
/* Instructions that take two arguments (register, integer). */
|
|
|
|
case CFA_offset:
|
|
|
|
case CFA_def_cfa:
|
|
|
|
if (cfi_parse_reg (¶m[0]) < 0)
|
|
|
|
{
|
|
|
|
as_bad (_("first argument to %s is not a register"),
|
|
|
|
cfi_insn_str (arg));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (cfi_parse_const (¶m[1]) < 0)
|
|
|
|
{
|
|
|
|
as_bad (_("second argument to %s is not a number"),
|
|
|
|
cfi_insn_str (arg));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Instructions that take one register argument. */
|
|
|
|
case CFA_def_cfa_register:
|
|
|
|
if (cfi_parse_reg (¶m[0]) < 0)
|
|
|
|
{
|
|
|
|
as_bad (_("argument to %s is not a register"), cfi_insn_str (arg));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Instructions that take one integer argument. */
|
|
|
|
case CFA_def_cfa_offset:
|
|
|
|
if (cfi_parse_const (¶m[0]) < 0)
|
|
|
|
{
|
|
|
|
as_bad (_("argument to %s is not a number"), cfi_insn_str (arg));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Special handling for pseudo-instruction. */
|
|
|
|
case CFI_adjust_cfa_offset:
|
|
|
|
if (cfi_parse_const (¶m[0]) < 0)
|
|
|
|
{
|
|
|
|
as_bad (_("argument to %s is not a number"),
|
|
|
|
".cfi_adjust_cfa_offset");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
param[0] += get_current_offset (cfi_info);
|
|
|
|
arg = CFA_def_cfa_offset;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
as_bad (_("unknown CFI instruction %d (%s)"), arg, cfi_insn_str (arg));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
cfi_add_insn (arg, param[0], param[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static symbolS *
|
|
|
|
cfi_get_label (void)
|
|
|
|
{
|
|
|
|
char symname[40], *symbase=".Llbl_cfi";
|
|
|
|
symbolS *symbolP;
|
|
|
|
unsigned int i = 0;
|
|
|
|
|
|
|
|
snprintf (symname, sizeof (symname), "%s_0x%lx",
|
|
|
|
symbase, (long) frag_now_fix ());
|
|
|
|
while ((symbolP = symbol_find (symname)))
|
|
|
|
{
|
|
|
|
if ((S_GET_VALUE (symbolP) == frag_now_fix ())
|
|
|
|
&& (S_GET_SEGMENT (symbolP) == now_seg))
|
|
|
|
{
|
|
|
|
return symbolP;
|
|
|
|
}
|
|
|
|
snprintf (symname, sizeof (symname), "%s_0x%lx_%u",
|
|
|
|
symbase, (long) frag_now_fix (), i++);
|
|
|
|
}
|
|
|
|
symbolP = (symbolS *) local_symbol_make (symname, now_seg,
|
|
|
|
(valueT) frag_now_fix (),
|
|
|
|
frag_now);
|
|
|
|
return symbolP;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dot_cfi_startproc (void)
|
|
|
|
{
|
|
|
|
if (cfi_info)
|
|
|
|
{
|
|
|
|
as_bad (_("previous CFI entry not closed (missing .cfi_endproc)"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
cfi_info = alloc_cfi_info ();
|
|
|
|
|
|
|
|
cfi_info->start_address = frag_now_fix ();
|
|
|
|
cfi_info->last_address = cfi_info->start_address;
|
|
|
|
cfi_info->labelname = S_GET_NAME (cfi_get_label ());
|
|
|
|
|
|
|
|
#ifdef tc_cfi_frame_initial_instructions
|
|
|
|
tc_cfi_frame_initial_instructions ();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#define cfi_is_advance_insn(insn) \
|
|
|
|
((insn >= CFA_set_loc && insn <= CFA_advance_loc4) \
|
|
|
|
|| insn == CFA_advance_loc)
|
|
|
|
|
|
|
|
enum data_types
|
|
|
|
{
|
|
|
|
t_ascii = 0,
|
|
|
|
t_byte = 1,
|
|
|
|
t_half = 2,
|
|
|
|
t_long = 4,
|
|
|
|
t_quad = 8,
|
|
|
|
t_uleb128 = 0x10,
|
|
|
|
t_sleb128 = 0x11
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Output CFI instructions to the file. */
|
|
|
|
|
|
|
|
static int
|
|
|
|
output_data (char **p, unsigned long *size, enum data_types type, long value)
|
|
|
|
{
|
|
|
|
char *ptr = *p;
|
|
|
|
unsigned int ret_size;
|
|
|
|
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case t_byte:
|
|
|
|
ret_size = 1;
|
|
|
|
break;
|
|
|
|
case t_half:
|
|
|
|
ret_size = 2;
|
|
|
|
break;
|
|
|
|
case t_long:
|
|
|
|
ret_size = 4;
|
|
|
|
break;
|
|
|
|
case t_quad:
|
|
|
|
case t_uleb128:
|
|
|
|
case t_sleb128:
|
|
|
|
ret_size = 8;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
as_warn (_("unknown type %d"), type);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*size < ret_size)
|
|
|
|
{
|
|
|
|
as_bad (_("output_data buffer is too small"));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case t_byte:
|
|
|
|
*ptr = (char) value;
|
|
|
|
if (verbose)
|
|
|
|
printf ("\t.byte\t0x%x\n", (unsigned char) *ptr);
|
|
|
|
break;
|
|
|
|
case t_half:
|
|
|
|
*(short *) ptr = (short) value & 0xFFFF;
|
|
|
|
if (verbose)
|
|
|
|
printf ("\t.half\t0x%x\n", (unsigned short) *ptr);
|
|
|
|
break;
|
|
|
|
case t_long:
|
|
|
|
*(int *) ptr = (int) value & 0xFFFFFFFF;
|
|
|
|
if (verbose)
|
|
|
|
printf ("\t.long\t0x%x\n", (unsigned int) *ptr);
|
|
|
|
break;
|
|
|
|
case t_quad:
|
|
|
|
*(long long *) ptr = (long long) value & 0xFFFFFFFF;
|
|
|
|
if (verbose)
|
|
|
|
printf ("\t.quad\t0x%x\n", (unsigned int) *ptr);
|
|
|
|
break;
|
|
|
|
case t_uleb128:
|
|
|
|
case t_sleb128:
|
|
|
|
ret_size = output_leb128 (ptr, value, type == t_sleb128);
|
|
|
|
if (verbose)
|
|
|
|
printf ("\t.%s\t0x%lx\n",
|
|
|
|
type == t_sleb128 ? "sleb128" : "uleb128",
|
|
|
|
value);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
as_warn ("unknown type %d", type);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
*size -= ret_size;
|
|
|
|
*p += ret_size;
|
|
|
|
|
|
|
|
return ret_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
cfi_output_insn (struct cfi_data *data, char **buf, unsigned long *buf_size)
|
|
|
|
{
|
|
|
|
char **pbuf = buf, *orig_buf = *buf;
|
|
|
|
unsigned long size;
|
|
|
|
|
|
|
|
if (!data || !buf)
|
|
|
|
as_fatal (_("cfi_output_insn called with NULL pointer"));
|
|
|
|
|
|
|
|
switch (data->insn)
|
|
|
|
{
|
|
|
|
case CFA_advance_loc:
|
|
|
|
if (verbose)
|
|
|
|
printf ("\t# %s(%ld)\n", cfi_insn_str (data->insn),
|
|
|
|
data->param[0]);
|
|
|
|
if (data->param[0] <= 0x3F)
|
|
|
|
{
|
|
|
|
output_data (pbuf, buf_size, t_byte, CFA_advance_loc +
|
|
|
|
(data->param[0] / current_config.code_align));
|
|
|
|
}
|
|
|
|
else if (data->param[0] <= 0xFF)
|
|
|
|
{
|
|
|
|
output_data (pbuf, buf_size, t_byte, CFA_advance_loc1);
|
|
|
|
output_data (pbuf, buf_size, t_byte,
|
|
|
|
data->param[0] / current_config.code_align);
|
|
|
|
}
|
|
|
|
else if (data->param[0] <= 0xFFFF)
|
|
|
|
{
|
|
|
|
output_data (pbuf, buf_size, t_byte, CFA_advance_loc2);
|
|
|
|
output_data (pbuf, buf_size, t_half,
|
|
|
|
data->param[0] / current_config.code_align);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
output_data (pbuf, buf_size, t_byte, CFA_advance_loc4);
|
|
|
|
output_data (pbuf, buf_size, t_long,
|
|
|
|
data->param[0] / current_config.code_align);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CFA_def_cfa:
|
|
|
|
if (verbose)
|
|
|
|
printf ("\t# CFA_def_cfa(%ld,%ld)\n", data->param[0], data->param[1]);
|
|
|
|
output_data (pbuf, buf_size, t_byte, CFA_def_cfa);
|
|
|
|
output_data (pbuf, buf_size, t_uleb128, data->param[0]);
|
|
|
|
output_data (pbuf, buf_size, t_uleb128, data->param[1]);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CFA_def_cfa_register:
|
|
|
|
case CFA_def_cfa_offset:
|
|
|
|
if (verbose)
|
|
|
|
printf ("\t# %s(%ld)\n", cfi_insn_str (data->insn),
|
|
|
|
data->param[0]);
|
|
|
|
output_data (pbuf, buf_size, t_byte, data->insn);
|
|
|
|
output_data (pbuf, buf_size, t_uleb128, data->param[0]);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CFA_offset:
|
|
|
|
if (verbose)
|
|
|
|
printf ("\t# %s(%ld,%ld)\n", cfi_insn_str (data->insn),
|
|
|
|
data->param[0], data->param[1]);
|
|
|
|
|
|
|
|
/* Check whether to use CFA_offset or CFA_offset_extended. */
|
|
|
|
if (data->param[0] <= 0x3F)
|
|
|
|
output_data (pbuf, buf_size, t_byte, CFA_offset + data->param[0]);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
output_data (pbuf, buf_size, t_byte, CFA_offset_extended);
|
|
|
|
output_data (pbuf, buf_size, t_uleb128, data->param[0]);
|
|
|
|
}
|
|
|
|
output_data (pbuf, buf_size, t_uleb128,
|
|
|
|
data->param[1] / current_config.data_align);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CFA_nop:
|
|
|
|
if (verbose)
|
|
|
|
printf ("\t# CFA_nop\n");
|
|
|
|
output_data (pbuf, buf_size, t_byte, CFA_nop);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
as_warn ("CFA_unknown[%d](%ld,%ld)", data->insn,
|
|
|
|
data->param[0], data->param[1]);
|
|
|
|
}
|
|
|
|
size = *pbuf - orig_buf;
|
|
|
|
*buf = *pbuf;
|
|
|
|
*buf_size -= size;
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dot_cfi_endproc (void)
|
|
|
|
{
|
|
|
|
struct cfi_data *data_ptr;
|
|
|
|
char *cie_buf, *fde_buf, *pbuf, *where;
|
|
|
|
unsigned long buf_size, cie_size, fde_size, last_cie_offset;
|
|
|
|
unsigned long fde_initloc_offset, fde_len_offset;
|
|
|
|
void *saved_seg, *cfi_seg;
|
2003-05-20 19:35:45 +08:00
|
|
|
expressionS exp;
|
2003-05-20 15:58:07 +08:00
|
|
|
|
|
|
|
if (! cfi_info)
|
|
|
|
{
|
|
|
|
as_bad (_(".cfi_endproc without corresponding .cfi_startproc"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
cfi_info->end_address = frag_now_fix ();
|
|
|
|
|
|
|
|
/* Open .eh_frame section. */
|
|
|
|
saved_seg = now_seg;
|
|
|
|
cfi_seg = subseg_new (".eh_frame", 0);
|
|
|
|
bfd_set_section_flags (stdoutput, cfi_seg,
|
|
|
|
SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA);
|
|
|
|
subseg_set (cfi_seg, 0);
|
|
|
|
|
|
|
|
/* Build CIE. */
|
|
|
|
cie_buf = xcalloc (1024, 1);
|
|
|
|
/* Skip space for CIE length. */
|
|
|
|
pbuf = cie_buf + 4;
|
|
|
|
buf_size = 1020;
|
|
|
|
|
|
|
|
if (verbose)
|
|
|
|
printf ("# CIE *****\n");
|
|
|
|
|
|
|
|
/* CIE id. */
|
|
|
|
output_data (&pbuf, &buf_size, t_long, 0x0);
|
|
|
|
/* Version. */
|
|
|
|
output_data (&pbuf, &buf_size, t_byte, 1);
|
|
|
|
/* Augmentation. */
|
|
|
|
output_data (&pbuf, &buf_size, t_byte, 0);
|
|
|
|
/* Code alignment. */
|
|
|
|
output_data (&pbuf, &buf_size, t_uleb128, current_config.code_align);
|
|
|
|
/* Data alignment. */
|
|
|
|
output_data (&pbuf, &buf_size, t_sleb128, current_config.data_align);
|
|
|
|
/* Return address column. */
|
|
|
|
output_data (&pbuf, &buf_size, t_byte, current_config.ra_column);
|
|
|
|
|
|
|
|
/* Build CFI instructions. */
|
|
|
|
data_ptr = cfi_info->data;
|
|
|
|
while (data_ptr && !cfi_is_advance_insn (data_ptr->insn))
|
|
|
|
{
|
|
|
|
cfi_output_insn (data_ptr, &pbuf, &buf_size);
|
|
|
|
data_ptr = data_ptr->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Align the whole data to current_config.eh_align. */
|
|
|
|
cie_size = pbuf - cie_buf;
|
|
|
|
cie_size += current_config.eh_align - cie_size % current_config.eh_align;
|
|
|
|
|
|
|
|
/* CIE length. */
|
|
|
|
pbuf = cie_buf;
|
|
|
|
output_data (&pbuf, &buf_size, t_long, cie_size - 4);
|
|
|
|
|
|
|
|
/* OK, we built the CIE. Let's write it to the file... */
|
|
|
|
last_cie_offset = frag_now_fix ();
|
|
|
|
where = (unsigned char *) frag_more (cie_size);
|
|
|
|
memcpy (where, cie_buf, cie_size);
|
|
|
|
|
|
|
|
/* Clean up. */
|
|
|
|
free (cie_buf);
|
|
|
|
|
|
|
|
/* Build the FDE... */
|
|
|
|
fde_buf = xcalloc (1024, 1);
|
|
|
|
pbuf = fde_buf;
|
|
|
|
buf_size = 1024;
|
|
|
|
|
|
|
|
if (verbose)
|
|
|
|
{
|
|
|
|
printf ("# FDE: start=0x%lx, end=0x%lx, delta=%d\n",
|
|
|
|
(long) cfi_info->start_address,
|
|
|
|
(long) cfi_info->end_address,
|
|
|
|
(int) (cfi_info->end_address - cfi_info->start_address));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* FDE length (t_long, 4 bytes) - will be set later. */
|
|
|
|
fde_len_offset = pbuf - fde_buf;
|
|
|
|
pbuf += 4;
|
|
|
|
buf_size -= 4;
|
|
|
|
|
|
|
|
/* CIE pointer - offset from here. */
|
|
|
|
output_data (&pbuf, &buf_size, t_long, cie_size + 4);
|
|
|
|
|
|
|
|
/* FDE initial location - this must be set relocatable! */
|
|
|
|
fde_initloc_offset = pbuf - fde_buf;
|
|
|
|
output_data (&pbuf, &buf_size, current_config.addr_length,
|
|
|
|
cfi_info->start_address);
|
|
|
|
|
|
|
|
/* FDE address range. */
|
|
|
|
output_data (&pbuf, &buf_size, current_config.addr_length,
|
|
|
|
cfi_info->end_address - cfi_info->start_address);
|
|
|
|
|
|
|
|
while (data_ptr)
|
|
|
|
{
|
|
|
|
cfi_output_insn (data_ptr, &pbuf, &buf_size);
|
|
|
|
data_ptr = data_ptr->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
fde_size = pbuf - fde_buf;
|
|
|
|
fde_size += current_config.eh_align - fde_size % current_config.eh_align;
|
|
|
|
|
|
|
|
/* Now we can set FDE length. */
|
|
|
|
pbuf = fde_buf + fde_len_offset;
|
|
|
|
buf_size = 4;
|
|
|
|
output_data (&pbuf, &buf_size, t_long, fde_size - 4);
|
|
|
|
|
|
|
|
/* Adjust initloc offset. */
|
|
|
|
fde_initloc_offset += frag_now_fix ();
|
|
|
|
|
|
|
|
/* Copy FDE to objfile. */
|
|
|
|
where = (unsigned char *) frag_more (fde_size);
|
|
|
|
memcpy (where, fde_buf, fde_size);
|
|
|
|
|
|
|
|
/* Set relocation for initial address. */
|
|
|
|
buf_size = current_config.addr_length;
|
|
|
|
memset (&exp, 0, sizeof (exp));
|
|
|
|
exp.X_op = O_symbol;
|
|
|
|
exp.X_add_symbol = symbol_find (cfi_info->labelname);
|
|
|
|
fix_new_exp (frag_now, fde_initloc_offset,
|
|
|
|
current_config.addr_length,
|
|
|
|
&exp, 0, current_config.reloc_type);
|
|
|
|
|
|
|
|
/* Clean up. */
|
|
|
|
free (fde_buf);
|
|
|
|
|
|
|
|
free (cfi_info);
|
|
|
|
cfi_info = NULL;
|
|
|
|
|
|
|
|
/* Restore previous segment. */
|
|
|
|
subseg_set (saved_seg, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
dot_cfi (int arg)
|
|
|
|
{
|
|
|
|
long param;
|
|
|
|
|
|
|
|
switch (arg)
|
|
|
|
{
|
|
|
|
case CFI_startproc:
|
|
|
|
dot_cfi_startproc ();
|
|
|
|
break;
|
|
|
|
case CFI_endproc:
|
|
|
|
dot_cfi_endproc ();
|
|
|
|
break;
|
|
|
|
case CFA_def_cfa:
|
|
|
|
case CFA_def_cfa_register:
|
|
|
|
case CFA_def_cfa_offset:
|
|
|
|
case CFA_offset:
|
|
|
|
case CFI_adjust_cfa_offset:
|
|
|
|
cfi_make_insn (arg);
|
|
|
|
break;
|
|
|
|
case CFI_verbose:
|
|
|
|
if (cfi_parse_const (¶m) >= 0)
|
|
|
|
verbose = (int) param;
|
|
|
|
else
|
|
|
|
verbose = 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
as_bad (_("unknown CFI code 0x%x (%s)"), arg, cfi_insn_str (arg));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ignore_rest_of_line ();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cfi_set_config (struct cfi_config *cfg)
|
|
|
|
{
|
|
|
|
assert (cfg != NULL);
|
|
|
|
assert (cfg->addr_length > 0);
|
|
|
|
|
|
|
|
current_config = *cfg;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
cfi_finish (void)
|
|
|
|
{
|
|
|
|
if (cfi_info)
|
|
|
|
as_bad (_("open CFI at the end of file; missing .cfi_endproc directive"));
|
|
|
|
}
|