#ifndef _LIBCRUN_
#include "lint.h"
#endif
#include <sys/types.h>
#include <limits.h>
#include "stack_unwind.h"
#include "unwind_context.h"
#include <dlfcn.h>
struct eh_frame_fields *
_Unw_Decode_FDE(struct eh_frame_fields *f, struct _Unwind_Context *ctx)
{
void *fde_data;
void *fde_end;
void *data;
ptrdiff_t reloc;
uintptr_t base;
void *cie_data;
void *cie_end;
void *cdata;
ptrdiff_t creloc;
int lsda_enc = 0;
int per_enc = 0;
int code_enc = 0;
char augment[8];
char *p;
uint64_t scratch;
uint64_t func = 0;
uint64_t range = 0;
_Unwind_Personality_Fn pfn = 0;
void* lsda = 0;
fde_data = ctx->fde;
data = fde_data;
fde_end = (void *)(((intptr_t)fde_data) + 4 +
_Unw_get_val(&data, 0, UNUM32, 1, 1, 0));
reloc = 0;
base = ((intptr_t)data) + reloc;
cie_data = (void *)(base - _Unw_get_val(&data, 0, UNUM32, 1, 1, 0));
cdata = cie_data;
cie_end = (void *)(((intptr_t)cie_data) + 4 +
_Unw_get_val(&cdata, 0, UNUM32, 1, 1, 0));
creloc = 0;
f->cie_ops_end = cie_end;
f->cie_reloc = creloc;
f->fde_ops_end = fde_end;
f->fde_reloc = reloc;
(void) _Unw_get_val(&cdata, creloc, UNUM32, 1, 1, 0);
(void) _Unw_get_val(&cdata, creloc, UNUM8, 1, 1, 0);
(*((uint64_t *)(&(augment[0])))) =
_Unw_get_val(&cdata, creloc, ZTSTRING, 1, 1, 0);
f->code_align = _Unw_get_val(&cdata, creloc, ULEB128, 1, 1, 0);
f->data_align = _Unw_get_val(&cdata, creloc, SLEB128, 1, 1, 0);
(void) _Unw_get_val(&cdata, creloc, UNUM8, 1, 1, 0);
if (augment[0] == 'z' &&
(scratch = _Unw_get_val(&cdata, creloc, ULEB128, 1, 1, 0)) != 0) {
for (p = &(augment[1]); *p != 0; p++) {
switch (*p) {
case 'P':
per_enc = _Unw_get_val(&cdata, creloc,
UNUM8, 1, 1, 0);
if (per_enc == 0)
per_enc = 0x4;
pfn = (_Unwind_Personality_Fn)
_Unw_get_val(&cdata, creloc,
ADDR, 1, 1, per_enc);
break;
case 'R':
code_enc = _Unw_get_val(&cdata, creloc,
UNUM8, 1, 1, 0);
break;
case 'L':
lsda_enc = _Unw_get_val(&cdata, creloc,
UNUM8, 1, 1, 0);
break;
}
}
}
if (code_enc == 0)
code_enc = 0x4;
func = _Unw_get_val(&data, reloc, ADDR, 1, 1, code_enc);
range = _Unw_get_val(&data, reloc, SIZE, 1, 1, code_enc);
if ((ctx->pc < func) || (ctx->pc > (func+range)))
return (0);
ctx->func = func;
ctx->range = range;
if (augment[0] == 'z') {
scratch = _Unw_get_val(&data, reloc, ULEB128, 1, 1, 0);
if (scratch == 4 && lsda_enc) {
lsda = (void *)_Unw_get_val(&data, reloc,
ADDR, 1, 1, lsda_enc);
} else if (scratch == 4) {
lsda = (void*)_Unw_get_val(&data, reloc,
ADDR, 1, 1, 0x1b);
} else if (scratch == 8) {
lsda = (void *)_Unw_get_val(&data, reloc,
ADDR, 1, 1, 0x4);
}
}
if (pfn)
ctx->pfn = pfn;
if (lsda)
ctx->lsda = lsda;
f->fde_ops = data;
f->cie_ops = cdata;
f->code_enc = code_enc;
return (f);
}
static int
table_ent_log_size(int enc)
{
int val = enc & 0xf;
int res;
switch (val) {
case 0x3:
res = 3;
break;
case 0x04:
res = 4;
break;
case 0x0b:
res = 3;
break;
case 0x0c:
res = 4;
break;
default:
break;
}
return (res);
}
static void
get_table_ent_val(unsigned char *data, unsigned char *data_end,
int enc, ptrdiff_t reloc, uintptr_t base,
uint64_t *codep, uint64_t *next_codep, void **fdep)
{
int val = enc & 0xf;
int rel = (enc >> 4) & 0xf;
unsigned char *second = data;
unsigned char *third = data;
uint64_t code;
void *fde;
uint64_t next_code;
switch (val) {
case 0x3:
code = (uint64_t)(*((uint32_t *)data));
second += 4;
fde = (void *)(uint64_t)(*((uint32_t *)second));
third += 8;
next_code = (third >= data_end)? ULONG_MAX :
(uint64_t)(*((uint32_t *)third));
break;
case 0x04:
code = (uint64_t)(*((uint64_t *)data));
second += 8;
fde = (void *)(uint64_t)(*((uint64_t *)second));
third += 16;
next_code = (third >= data_end)? ULONG_MAX :
(uint64_t)(*((uint64_t *)third));
break;
case 0x0b:
code = (uint64_t)(int64_t)(*((int32_t *)data));
second += 4;
fde = (void *)(uint64_t)(int64_t)(*((int32_t *)second));
third += 8;
next_code = (third >= data_end)? ULONG_MAX :
(uint64_t)(int64_t)(*((int32_t *)third));
break;
case 0x0c:
code = (uint64_t)(*((int64_t *)data));
second += 8;
fde = (void *)(uint64_t)(*((int64_t *)second));
third += 16;
next_code = (third >= data_end)? ULONG_MAX :
(uint64_t)(*((int64_t *)third));
break;
}
switch (rel) {
case 0:
break;
case 1:
code += (uint64_t)data + reloc;
fde = (void *)(((uint64_t)fde) + (uint64_t)second + reloc);
if (next_code != ULONG_MAX)
next_code += (uint64_t)third + reloc;
break;
case 3:
code += base;
fde = (void *)(((uint64_t)fde) + base);
if (next_code != ULONG_MAX)
next_code += base;
break;
default:
break;
}
*codep = code;
*fdep = fde;
*next_codep = next_code;
}
static void *
locate_fde_for_pc(uint64_t pc, int enc,
unsigned char *table, unsigned char *table_end,
ptrdiff_t reloc, uintptr_t base);
void *
_Unw_EhfhLookup(struct _Unwind_Context *ctx)
{
Dl_amd64_unwindinfo dlef;
void* data;
void* data_end;
uint64_t pc = ctx->pc;
int fp_enc, fc_enc, ft_enc;
unsigned char *pi, *pj;
ptrdiff_t reloc;
uintptr_t base;
dlef.dlui_version = 1;
if (0 == dlamd64getunwind((void*)pc, &dlef)) {
return (0);
}
data = dlef.dlui_unwindstart;
if (0 == data)
return (0);
base = (uintptr_t)data;
data_end = dlef.dlui_unwindend;
reloc = 0;
(void) _Unw_get_val(&data, reloc, UNUM8, 1, 1, 0);
fp_enc = _Unw_get_val(&data, reloc, UNUM8, 1, 1, 0);
fc_enc = _Unw_get_val(&data, reloc, UNUM8, 1, 1, 0);
ft_enc = _Unw_get_val(&data, reloc, UNUM8, 1, 1, 0);
(void) _Unw_get_val(&data, reloc, ADDR, 1, 1, fp_enc);
(void) _Unw_get_val(&data, reloc, SIZE, 1, 1, fc_enc);
pi = data;
pj = data_end;
ctx->fde = locate_fde_for_pc(pc, ft_enc, pi, pj, reloc, base);
return ((void *)(ctx->fde));
}
static void *
locate_fde_for_pc(uint64_t pc, int enc,
unsigned char *table_bg, unsigned char *table_end,
ptrdiff_t reloc, uintptr_t base)
{
unsigned char *pi = table_bg;
unsigned char *pj = table_end;
uint64_t range_start, range_end;
void* fde;
int log_size = table_ent_log_size(enc);
while (pi < pj) {
unsigned char *pr =
pi + (((pj - pi) >> (log_size + 1)) << log_size);
get_table_ent_val(pr, table_end, enc, reloc, base,
&range_start, &range_end, &fde);
if (range_start <= pc && pc < range_end) {
return ((void*) fde);
}
if (range_start < pc)
pi = pr + (1 << log_size);
else
pj = pr;
}
return (0);
}