#include "as.h"
#include "subsegs.h"
#include "elf/dwarf2.h"
struct cie_info
{
unsigned code_alignment;
int z_augmentation;
};
static int get_cie_info (struct cie_info *);
static int
get_cie_info (struct cie_info *info)
{
fragS *f;
fixS *fix;
int offset;
char CIE_id;
char augmentation[10];
int iaug;
int code_alignment = 0;
#if defined (BFD_ASSEMBLER) || defined (MANY_SEGMENTS)
f = seg_info (now_seg)->frchainP->frch_root;
#else
f = frchain_now->frch_root;
#endif
#ifdef BFD_ASSEMBLER
fix = seg_info (now_seg)->frchainP->fix_root;
#else
fix = *seg_fix_rootP;
#endif
if (strcmp (segment_name (now_seg), ".debug_frame") == 0)
CIE_id = (char)0xff;
else
CIE_id = 0;
offset = 4;
while (f != NULL && offset >= f->fr_fix)
{
offset -= f->fr_fix;
f = f->fr_next;
}
if (f == NULL
|| f->fr_fix - offset < 4
|| f->fr_literal[offset] != CIE_id
|| f->fr_literal[offset + 1] != CIE_id
|| f->fr_literal[offset + 2] != CIE_id
|| f->fr_literal[offset + 3] != CIE_id)
return 0;
offset += 4;
while (f != NULL && offset >= f->fr_fix)
{
offset -= f->fr_fix;
f = f->fr_next;
}
if (f == NULL
|| f->fr_fix - offset < 1
|| f->fr_literal[offset] != 1)
return 0;
iaug = 0;
++offset;
while (1)
{
while (f != NULL && offset >= f->fr_fix)
{
offset -= f->fr_fix;
f = f->fr_next;
}
if (f == NULL)
return 0;
while (offset < f->fr_fix && f->fr_literal[offset] != '\0')
{
if ((size_t) iaug < (sizeof augmentation) - 1)
{
augmentation[iaug] = f->fr_literal[offset];
++iaug;
}
++offset;
}
if (offset < f->fr_fix)
break;
}
++offset;
while (f != NULL && offset >= f->fr_fix)
{
offset -= f->fr_fix;
f = f->fr_next;
}
if (f == NULL)
return 0;
augmentation[iaug] = '\0';
if (augmentation[0] == '\0')
{
}
else if (strcmp (augmentation, "eh") == 0)
{
while (fix != NULL
&& (fix->fx_frag != f || fix->fx_where != offset))
fix = fix->fx_next;
if (fix == NULL)
offset += 4;
else
offset += fix->fx_size;
while (f != NULL && offset >= f->fr_fix)
{
offset -= f->fr_fix;
f = f->fr_next;
}
if (f == NULL)
return 0;
}
else if (augmentation[0] != 'z')
return 0;
code_alignment = f->fr_literal[offset] & 0xff;
if ((code_alignment & 0x80) != 0)
code_alignment = 0;
info->code_alignment = code_alignment;
info->z_augmentation = (augmentation[0] == 'z');
return 1;
}
int
check_eh_frame (expressionS *exp, unsigned int *pnbytes)
{
struct frame_data
{
enum frame_state
{
state_idle,
state_saw_size,
state_saw_cie_offset,
state_saw_pc_begin,
state_seeing_aug_size,
state_skipping_aug,
state_wait_loc4,
state_saw_loc4,
state_error,
} state;
int cie_info_ok;
struct cie_info cie_info;
symbolS *size_end_sym;
fragS *loc4_frag;
int loc4_fix;
int aug_size;
int aug_shift;
};
static struct frame_data eh_frame_data;
static struct frame_data debug_frame_data;
struct frame_data *d;
if (flag_traditional_format)
return 0;
if (strcmp (segment_name (now_seg), ".eh_frame") == 0)
d = &eh_frame_data;
else if (strcmp (segment_name (now_seg), ".debug_frame") == 0)
d = &debug_frame_data;
else
return 0;
if (d->state >= state_saw_size && S_IS_DEFINED (d->size_end_sym))
{
d->state = state_idle;
}
switch (d->state)
{
case state_idle:
if (*pnbytes == 4)
{
if ((exp->X_op == O_symbol || exp->X_op == O_subtract)
&& ! S_IS_DEFINED (exp->X_add_symbol))
{
d->state = state_saw_size;
d->size_end_sym = exp->X_add_symbol;
}
}
break;
case state_saw_size:
case state_saw_cie_offset:
d->state += 1;
break;
case state_saw_pc_begin:
if (! d->cie_info_ok
&& ! (d->cie_info_ok = get_cie_info (&d->cie_info)))
d->state = state_error;
else if (d->cie_info.z_augmentation)
{
d->state = state_seeing_aug_size;
d->aug_size = 0;
d->aug_shift = 0;
}
else
d->state = state_wait_loc4;
break;
case state_seeing_aug_size:
if ((int)*pnbytes == -1 && exp->X_op == O_constant)
{
d->aug_size = exp->X_add_number;
d->state = state_skipping_aug;
}
else if (*pnbytes == 1 && exp->X_op == O_constant)
{
unsigned char byte = exp->X_add_number;
d->aug_size |= (byte & 0x7f) << d->aug_shift;
d->aug_shift += 7;
if ((byte & 0x80) == 0)
d->state = state_skipping_aug;
}
else
d->state = state_error;
if (d->state == state_skipping_aug && d->aug_size == 0)
d->state = state_wait_loc4;
break;
case state_skipping_aug:
if ((int)*pnbytes < 0)
d->state = state_error;
else
{
int left = (d->aug_size -= *pnbytes);
if (left == 0)
d->state = state_wait_loc4;
else if (left < 0)
d->state = state_error;
}
break;
case state_wait_loc4:
if (*pnbytes == 1
&& exp->X_op == O_constant
&& exp->X_add_number == DW_CFA_advance_loc4)
{
frag_grow (1);
d->state = state_saw_loc4;
d->loc4_frag = frag_now;
d->loc4_fix = frag_now_fix ();
}
break;
case state_saw_loc4:
d->state = state_wait_loc4;
if (*pnbytes != 4)
break;
if (exp->X_op == O_constant)
{
if (d->cie_info.code_alignment > 0
&& exp->X_add_number % d->cie_info.code_alignment == 0
&& exp->X_add_number / d->cie_info.code_alignment < 0x40)
{
d->loc4_frag->fr_literal[d->loc4_fix]
= DW_CFA_advance_loc
| (exp->X_add_number / d->cie_info.code_alignment);
return 1;
}
else if (exp->X_add_number < 0x100)
{
d->loc4_frag->fr_literal[d->loc4_fix] = DW_CFA_advance_loc1;
*pnbytes = 1;
}
else if (exp->X_add_number < 0x10000)
{
d->loc4_frag->fr_literal[d->loc4_fix] = DW_CFA_advance_loc2;
*pnbytes = 2;
}
}
else if (exp->X_op == O_subtract)
{
int fr_subtype;
if (d->cie_info.code_alignment > 0)
fr_subtype = d->cie_info.code_alignment << 3;
else
fr_subtype = 0;
frag_var (rs_cfa, 4, 0, fr_subtype, make_expr_symbol (exp),
d->loc4_fix, (char *) d->loc4_frag);
return 1;
}
break;
case state_error:
break;
}
return 0;
}
int
eh_frame_estimate_size_before_relax (fragS *frag)
{
offsetT diff;
int ca = frag->fr_subtype >> 3;
int ret;
diff = resolve_symbol_value (frag->fr_symbol);
if (ca > 0 && diff % ca == 0 && diff / ca < 0x40)
ret = 0;
else if (diff < 0x100)
ret = 1;
else if (diff < 0x10000)
ret = 2;
else
ret = 4;
frag->fr_subtype = (frag->fr_subtype & ~7) | ret;
return ret;
}
int
eh_frame_relax_frag (fragS *frag)
{
int oldsize, newsize;
oldsize = frag->fr_subtype & 7;
newsize = eh_frame_estimate_size_before_relax (frag);
return newsize - oldsize;
}
void
eh_frame_convert_frag (fragS *frag)
{
offsetT diff;
fragS *loc4_frag;
int loc4_fix;
loc4_frag = (fragS *) frag->fr_opcode;
loc4_fix = (int) frag->fr_offset;
diff = resolve_symbol_value (frag->fr_symbol);
switch (frag->fr_subtype & 7)
{
case 0:
{
int ca = frag->fr_subtype >> 3;
assert (ca > 0 && diff % ca == 0 && diff / ca < 0x40);
loc4_frag->fr_literal[loc4_fix] = DW_CFA_advance_loc | (diff / ca);
}
break;
case 1:
assert (diff < 0x100);
loc4_frag->fr_literal[loc4_fix] = DW_CFA_advance_loc1;
frag->fr_literal[frag->fr_fix] = diff;
break;
case 2:
assert (diff < 0x10000);
loc4_frag->fr_literal[loc4_fix] = DW_CFA_advance_loc2;
md_number_to_chars (frag->fr_literal + frag->fr_fix, diff, 2);
break;
default:
md_number_to_chars (frag->fr_literal + frag->fr_fix, diff, 4);
break;
}
frag->fr_fix += frag->fr_subtype & 7;
frag->fr_type = rs_fill;
frag->fr_subtype = 0;
frag->fr_offset = 0;
}