#pragma ident "@(#)dt_isadep.c 1.14 06/02/22 SMI"
#if defined(__arm__) || defined(__armv6__) || defined(__arm64__)
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <libgen.h>
#include <dt_impl.h>
#include <dt_pid.h>
#define SIGNEXTEND(x,v) ((((int) (x)) << (32-v)) >> (32-v))
#define ALIGNADDR(x,v) (((x) >> v) << v)
#define TYPE_SAME 0
#define TYPE_TEXT 1
#define TYPE_DATA 2
int
dt_pid_create_entry_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp,
fasttrap_probe_spec_t *ftp, const GElf_Sym *symp)
{
char dmodel = Pstatus(P)->pr_dmodel;
ftp->ftps_probe_type = DTFTP_ENTRY;
ftp->ftps_pc = symp->st_value;
if (dmodel == PR_MODEL_LP64) {
ftp->ftps_arch_subinfo = 0;
} else {
if (symp->st_arch_subinfo == 0) {
dt_dprintf("No arm/thumb information for %s:%s, not instrumenting\n",ftp->ftps_mod,ftp->ftps_func);
return (1);
} else {
ftp->ftps_arch_subinfo = symp->st_arch_subinfo;
}
}
ftp->ftps_size = (size_t)symp->st_size;
ftp->ftps_noffs = 1;
ftp->ftps_offs[0] = 0;
if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) {
dt_dprintf("fasttrap probe creation ioctl failed: %s\n",
strerror(errno));
return (dt_set_errno(dtp, errno));
}
return (1);
}
static int
dt_pid_create_return_probe32(struct ps_prochandle *P, dtrace_hdl_t *dtp,
fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, uint64_t *stret)
{
uint8_t *text;
uint8_t *constants;
ftp->ftps_probe_type = DTFTP_RETURN;
ftp->ftps_pc = symp->st_value;
if (symp->st_arch_subinfo == 0) {
dt_dprintf("No arm/thumb information for %s:%s, not instrumenting\n",ftp->ftps_mod,ftp->ftps_func);
return (1);
} else {
ftp->ftps_arch_subinfo = symp->st_arch_subinfo;
}
ftp->ftps_size = (size_t)symp->st_size;
ftp->ftps_noffs = 0;
if ((text = calloc(1, symp->st_size + 4)) == NULL) {
dt_dprintf("mr sparkle: malloc() failed\n");
return (DT_PROC_ERR);
}
if ((constants = calloc(1, symp->st_size + 4)) == NULL) {
dt_dprintf("mr sparkle: malloc() failed\n");
free(text);
return (DT_PROC_ERR);
}
if ((ftp->ftps_arch_subinfo == 2) && (symp->st_value & 2))
text = text + 2;
if (Pread(P, text, symp->st_size, symp->st_value) != symp->st_size) {
dt_dprintf("mr sparkle: Pread() failed\n");
free(text);
free(constants);
return (DT_PROC_ERR);
}
if (ftp->ftps_arch_subinfo == 1) {
uint32_t* limit = (uint32_t*) (text + ftp->ftps_size);
uint32_t* inst = (uint32_t*) text;
ulong_t offset;
int mode = TYPE_TEXT;
while (inst < limit) {
offset = (ulong_t) inst - (ulong_t) text;
if ((mode == TYPE_DATA && constants[offset] == TYPE_SAME) || constants[offset] == TYPE_DATA) {
inst++;
mode = TYPE_DATA;
continue;
}
if (constants[offset] == TYPE_TEXT)
mode = TYPE_TEXT;
if ((*inst & 0xF0000000) == 0xF0000000) {
inst++;
continue;
}
if ((*inst & 0x0F7F0000) == 0x051F0000) {
uint32_t displacement = *inst & 0xFFF;
ulong_t target = ((uint32_t) inst) + 8;
if (*inst & (1 << 23)) {
target += displacement;
} else {
target -= displacement;
}
if (((ulong_t) inst) < target && target < ((ulong_t) limit))
constants[(ulong_t) target - (ulong_t) text] = TYPE_DATA;
}
if ((*inst & 0x0E50F000) == 0x0410F000) {
if (inst > text) {
uint32_t prevInst = *(inst-1);
if ((prevInst & 0xF0000000) != (*inst & 0xF0000000) ||
(prevInst & 0x0FFFFFFF) != 0x01A0E00F)
goto is_ret_arm;
} else {
goto is_ret_arm;
}
} else if ((*inst & 0x0FFFFFF0) == 0x01A0F000) {
goto is_ret_arm;
} else if ((*inst & 0x0FFFF000) == 0x03A0F000) {
goto is_ret_arm;
} else if ((*inst & 0x0FFFFFF0) == 0x012FFF10) {
goto is_ret_arm;
} else if ((*inst & 0x0F000000) == 0x0A000000) {
ulong_t target = ((ulong_t) inst) + 8 + (SIGNEXTEND(*inst & 0x00FFFFFF,24) << 2);
if (((ulong_t) inst) < target && target < ((ulong_t) limit))
constants[(ulong_t) target - (ulong_t) text] = TYPE_TEXT;
if (target < text || limit <= target)
goto is_ret_arm;
} else if ((*inst & 0x0FFF8000) == 0x08BD8000) {
goto is_ret_arm;
}
inst++;
continue;
is_ret_arm:
offset = (ulong_t) inst - (ulong_t) text;
dt_dprintf("return at offset %lx Arm\n", offset);
ftp->ftps_offs[ftp->ftps_noffs++] = offset;
inst++;
}
} else if (ftp->ftps_arch_subinfo == 2) {
uint16_t* limit = (uint16_t*) (text + ftp->ftps_size);
uint16_t* inst = (uint16_t*) text;
ulong_t offset;
int mode = TYPE_TEXT;
while (inst < limit) {
offset = (ulong_t) inst - (ulong_t) text;
if ((mode == TYPE_DATA && constants[offset] == TYPE_SAME) || constants[offset] == TYPE_DATA) {
inst += 2;
mode = TYPE_DATA;
continue;
}
if (constants[offset] == TYPE_TEXT)
mode = TYPE_TEXT;
if (((*inst >> 11) & 0x1F) > 0x1C) {
uint16_t* inst2 = inst+1;
if ((*inst & 0xFF7F) == 0xF85F) {
uint32_t displacement = *inst2 & 0xFFF;
ulong_t target = ALIGNADDR(((uint32_t) inst)+4, 2);
if (*inst & (1 << 7)) {
target += displacement;
} else {
target -= displacement;
}
if (((ulong_t) inst) < target && target < ((ulong_t) limit))
constants[(ulong_t) target - (ulong_t) text] = TYPE_DATA;
} else if ((*inst & 0xFF3F) == 0xED1F && (*inst2 & 0x0E00) == 0x0A00) {
uint32_t displacement = (*inst2 * 0xFF) << 2;
ulong_t target = ALIGNADDR(((uint32_t) inst)+4, 2);
if (*inst & (1 << 7)) {
target += displacement;
} else {
target -= displacement;
}
if (((ulong_t) inst) < target && target < ((ulong_t) limit))
constants[(ulong_t) target - (ulong_t) text] = TYPE_DATA;
}
if ((*inst & 0xF800) == 0xF000 && (*inst2 & 0xD000) == 0x8000) {
int cond = (*inst >> 6) & 0xF;
if (cond != 0xE && cond != 0xF) {
int S = (*inst >> 10) & 1, J1 = (*inst2 >> 13) & 1, J2 = (*inst2 >> 11) & 1;
ulong_t target = ((ulong_t) inst) + 4 + SIGNEXTEND(
(S << 20) | (J2 << 19) | (J1 << 18) |
((*inst & 0x003F) << 12) | ((*inst2 & 0x07FF) << 1),
21);
if (((ulong_t) inst) < target && target < ((ulong_t) limit))
constants[(ulong_t) target - (ulong_t) text] = TYPE_TEXT;
if (target < text || limit <= target)
goto is_ret_thumb2;
}
} else if ((*inst & 0xF800) == 0xF000 && (*inst2 & 0xD000) == 0x9000) {
int S = (*inst >> 10) & 1, J1 = (*inst2 >> 13) & 1, J2 = (*inst2 >> 11) & 1;
int I1 = (J1 != S) ? 0 : 1, I2 = (J2 != S) ? 0 : 1;
ulong_t target = ((ulong_t) inst) + 4 + SIGNEXTEND(
(S << 24) | (I1 << 23) | (I2 << 22) |
((*inst & 0x3FF) << 12) | ((*inst2 & 0x7FF) << 1),
25);
if (((ulong_t) inst) < target && target < ((ulong_t) limit))
constants[(ulong_t) target - (ulong_t) text] = TYPE_TEXT;
if (target < text || limit <= target)
goto is_ret_thumb2;
} else if (*inst == 0xE8BD && (*inst2 & 0x8000) == 0x8000) {
goto is_ret_thumb2;
} else if ((*inst & 0xFF70) == 0xF850 && (*inst2 & 0xF000) == 0xF000) {
goto is_ret_thumb2;
}
inst += 2;
continue;
is_ret_thumb2:
offset = (ulong_t) inst - (ulong_t) text;
dt_dprintf("return at offset %lx Thumb32\n", offset);
ftp->ftps_offs[ftp->ftps_noffs++] = offset;
inst += 2;
} else {
if ((*inst & 0xF800) == 0x4800) {
ulong_t target = ALIGNADDR(((uint32_t) inst) + ((*inst & 0xFF) * 4) + 4, 2);
if (((ulong_t) inst) < target && target < ((ulong_t) limit))
constants[(ulong_t) target - (ulong_t) text] = TYPE_DATA;
}
if ((*inst & 0xFF87) == 0x4687) {
goto is_ret_thumb;
} else if ((*inst & 0xFF87) == 0x4700) {
goto is_ret_thumb;
} else if ((*inst & 0xF000) == 0xD000) {
int cond = (*inst >> 8) & 0xF;
if (cond != 0xE && cond != 0xF) {
ulong_t target = ((ulong_t) inst) + 4 + (SIGNEXTEND(*inst & 0xFF,8) << 1);
if (((ulong_t) inst) < target && target < ((ulong_t) limit))
constants[(ulong_t) target - (ulong_t) text] = TYPE_TEXT;
if (target < text || limit <= target)
goto is_ret_thumb;
}
} else if ((*inst & 0xF800) == 0xE000) {
ulong_t target = ((ulong_t) inst) + 4 + (SIGNEXTEND(*inst & 0x7FF,11) << 1);
if (((ulong_t) inst) < target && target < ((ulong_t) limit))
constants[(ulong_t) target - (ulong_t) text] = TYPE_TEXT;
if (target < text || limit <= target)
goto is_ret_thumb;
} else if ((*inst & 0xFF00) == 0xBD00) {
goto is_ret_thumb;
}
inst++;
continue;
is_ret_thumb:
offset = (ulong_t) inst - (ulong_t) text;
dt_dprintf("return at offset %lx Thumb16\n", offset);
ftp->ftps_offs[ftp->ftps_noffs++] = offset;
inst++;
}
}
}
if ((ftp->ftps_arch_subinfo == 2) && (symp->st_value & 2))
text = text - 2;
free(text);
free(constants);
if (ftp->ftps_noffs > 0) {
if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) {
dt_dprintf("fasttrap probe creation ioctl failed: %s\n",
strerror(errno));
return (dt_set_errno(dtp, errno));
}
}
return (ftp->ftps_noffs);
}
static int
dt_pid_create_return_probe64(struct ps_prochandle *P, dtrace_hdl_t *dtp,
fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, uint64_t *stret)
{
uint8_t *text;
ftp->ftps_probe_type = DTFTP_RETURN;
ftp->ftps_pc = symp->st_value;
ftp->ftps_arch_subinfo = 0;
ftp->ftps_size = (size_t)symp->st_size;
ftp->ftps_noffs = 0;
if ((text = calloc(1, symp->st_size + 4)) == NULL) {
dt_dprintf("mr sparkle: malloc() failed\n");
return (DT_PROC_ERR);
}
if (Pread(P, text, symp->st_size, symp->st_value) != symp->st_size) {
dt_dprintf("mr sparkle: Pread() failed\n");
free(text);
return (DT_PROC_ERR);
}
uint32_t* limit = (uint32_t*) (text + ftp->ftps_size);
uint32_t* inst = (uint32_t*) text;
while (inst < limit) {
if (((*inst & 0xfc000000) == 0x14000000)) {
uint32_t target = ((uint32_t) inst) + 4 * SIGNEXTEND(*inst & ((1 << 26) - 1), 26);
if (target < (uint32_t) text || (uint32_t) limit <= target) {
goto is_ret;
}
}
if (*inst == 0xd65f03c0) {
goto is_ret;
}
inst++;
continue;
is_ret:
dt_dprintf("return at offset %lx Arm64\n", (ulong_t) inst - (ulong_t) text);
ftp->ftps_offs[ftp->ftps_noffs++] = (ulong_t) inst - (ulong_t) text;
inst++;
}
free(text);
if (ftp->ftps_noffs > 0) {
if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) {
dt_dprintf("fasttrap probe creation ioctl failed: %s\n",
strerror(errno));
return (dt_set_errno(dtp, errno));
}
}
return (ftp->ftps_noffs);
}
int
dt_pid_create_return_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp,
fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, uint64_t *stret)
{
char dmodel = Pstatus(P)->pr_dmodel;
if (dmodel == PR_MODEL_LP64) {
return dt_pid_create_return_probe64(P, dtp, ftp, symp, stret);
} else {
return dt_pid_create_return_probe32(P, dtp, ftp, symp, stret);
}
}
int
dt_pid_create_offset_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp,
fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, ulong_t off)
{
return (1);
}
int
dt_pid_create_glob_offset_probes(struct ps_prochandle *P, dtrace_hdl_t *dtp,
fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, const char *pattern)
{
return (-1);
}
#endif // __arm__ || __armv6__ || __arm64__