mirror of
https://github.com/sstefani/mtrace.git
synced 2025-12-06 08:46:41 +08:00
remame the toll in mtrace-ng add new long dump optios, which shows also the libraries minor fixes
350 lines
8.8 KiB
C
350 lines
8.8 KiB
C
/*
|
|
* This file is part of mtrace-ng.
|
|
* Copyright (C) 2015 Stefani Seibold <stefani@seibold.net>
|
|
* This file is based on the ltrace source
|
|
*
|
|
* This work was sponsored by Rohde & Schwarz GmbH & Co. KG, Munich/Germany.
|
|
*
|
|
* This program 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 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program 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 this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
|
* 02110-1301 USA
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <inttypes.h>
|
|
#include <sys/reg.h>
|
|
|
|
#include "common.h"
|
|
#include "backend.h"
|
|
#include "debug.h"
|
|
#include "dwarf.h"
|
|
#include "library.h"
|
|
#include "task.h"
|
|
|
|
struct arch_reg {
|
|
unsigned int ip;
|
|
unsigned int sp;
|
|
unsigned int bp;
|
|
};
|
|
|
|
enum dwarf_x86_32_regnum {
|
|
DWARF_X86_EAX,
|
|
DWARF_X86_ECX,
|
|
DWARF_X86_EDX,
|
|
DWARF_X86_EBX,
|
|
DWARF_X86_ESP,
|
|
DWARF_X86_EBP,
|
|
DWARF_X86_ESI,
|
|
DWARF_X86_EDI,
|
|
DWARF_X86_EIP,
|
|
};
|
|
|
|
#ifndef __x86_64__
|
|
static const uint8_t dwarf_to_regnum_map32[] = {
|
|
[DWARF_X86_EAX] = EAX,
|
|
[DWARF_X86_ECX] = ECX,
|
|
[DWARF_X86_EDX] = EDX,
|
|
[DWARF_X86_EBX] = EBX,
|
|
[DWARF_X86_ESP] = UESP,
|
|
[DWARF_X86_EBP] = EBP,
|
|
[DWARF_X86_ESI] = ESI,
|
|
[DWARF_X86_EDI] = EDI,
|
|
[DWARF_X86_EIP] = EIP,
|
|
};
|
|
#else
|
|
static const uint8_t dwarf_to_regnum_map32[] = {
|
|
[DWARF_X86_EAX] = RAX,
|
|
[DWARF_X86_ECX] = RCX,
|
|
[DWARF_X86_EDX] = RDX,
|
|
[DWARF_X86_EBX] = RBX,
|
|
[DWARF_X86_ESP] = RSP,
|
|
[DWARF_X86_EBP] = RBP,
|
|
[DWARF_X86_ESI] = RSI,
|
|
[DWARF_X86_EDI] = RDI,
|
|
[DWARF_X86_EIP] = RIP,
|
|
};
|
|
|
|
enum dwarf_x86_64_regnum {
|
|
DWARF_X86_RAX,
|
|
DWARF_X86_RDX,
|
|
DWARF_X86_RCX,
|
|
DWARF_X86_RBX,
|
|
DWARF_X86_RSI,
|
|
DWARF_X86_RDI,
|
|
DWARF_X86_RBP,
|
|
DWARF_X86_RSP,
|
|
DWARF_X86_R8,
|
|
DWARF_X86_R9,
|
|
DWARF_X86_R10,
|
|
DWARF_X86_R11,
|
|
DWARF_X86_R12,
|
|
DWARF_X86_R13,
|
|
DWARF_X86_R14,
|
|
DWARF_X86_R15,
|
|
DWARF_X86_RIP,
|
|
};
|
|
|
|
static const uint8_t dwarf_to_regnum_map64[] = {
|
|
[DWARF_X86_RAX] = RAX,
|
|
[DWARF_X86_RDX] = RDX,
|
|
[DWARF_X86_RCX] = RCX,
|
|
[DWARF_X86_RBX] = RBX,
|
|
[DWARF_X86_RSI] = RSI,
|
|
[DWARF_X86_RDI] = RDI,
|
|
[DWARF_X86_RBP] = RBP,
|
|
[DWARF_X86_RSP] = RSP,
|
|
[DWARF_X86_R8] = R8,
|
|
[DWARF_X86_R9] = R9,
|
|
[DWARF_X86_R10] = R10,
|
|
[DWARF_X86_R11] = R11,
|
|
[DWARF_X86_R12] = R12,
|
|
[DWARF_X86_R13] = R13,
|
|
[DWARF_X86_R14] = R14,
|
|
[DWARF_X86_R15] = R15,
|
|
[DWARF_X86_RIP] = RIP,
|
|
};
|
|
|
|
static const struct arch_reg arch_reg64 = {
|
|
.ip = DWARF_X86_RIP,
|
|
.sp = DWARF_X86_RSP,
|
|
.bp = DWARF_X86_RBP,
|
|
};
|
|
#endif
|
|
|
|
static const struct arch_reg arch_reg32 = {
|
|
.ip = DWARF_X86_EIP,
|
|
.sp = DWARF_X86_ESP,
|
|
.bp = DWARF_X86_EBP,
|
|
};
|
|
|
|
static int is_signal_frame(struct dwarf_cursor *c)
|
|
{
|
|
return c->dci.signal_frame;
|
|
}
|
|
|
|
static int is_plt_entry(struct dwarf_addr_space *as)
|
|
{
|
|
#if 0
|
|
struct dwarf_cursor *c = &as->cursor;
|
|
uint8_t data[12];
|
|
|
|
if (copy_from_proc(c->task, c->ip, data, sizeof(data)) != sizeof(data)) {
|
|
debug(DEBUG_DWARF, "cannot access memory %#lx of pid %d", c->ip, c->task->pid);
|
|
return 0;
|
|
}
|
|
|
|
if (data[0] == 0xff && data[1] == 0x25 && data[6] == 0x68 && data[11] == 0xe9)
|
|
return 1;
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int dwarf_arch_init(struct dwarf_addr_space *as)
|
|
{
|
|
#ifdef __x86_64__
|
|
if (as->is_64bit) {
|
|
as->ip_reg = arch_reg64.ip;
|
|
as->ret_reg = arch_reg64.sp;
|
|
as->num_regs = ARRAY_SIZE(dwarf_to_regnum_map64);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
as->ip_reg = arch_reg32.ip;
|
|
as->ret_reg = arch_reg32.sp;
|
|
as->num_regs = ARRAY_SIZE(dwarf_to_regnum_map32);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int dwarf_arch_init_unwind(struct dwarf_addr_space *as)
|
|
{
|
|
struct dwarf_cursor *c = &as->cursor;
|
|
|
|
#ifdef __x86_64__
|
|
if (as->is_64bit) {
|
|
c->loc[DWARF_X86_RAX] = DWARF_REG_LOC(DWARF_X86_RAX);
|
|
c->loc[DWARF_X86_RDX] = DWARF_REG_LOC(DWARF_X86_RDX);
|
|
c->loc[DWARF_X86_RCX] = DWARF_REG_LOC(DWARF_X86_RCX);
|
|
c->loc[DWARF_X86_RBX] = DWARF_REG_LOC(DWARF_X86_RBX);
|
|
c->loc[DWARF_X86_RSI] = DWARF_REG_LOC(DWARF_X86_RSI);
|
|
c->loc[DWARF_X86_RDI] = DWARF_REG_LOC(DWARF_X86_RDI);
|
|
c->loc[DWARF_X86_RBP] = DWARF_REG_LOC(DWARF_X86_RBP);
|
|
c->loc[DWARF_X86_RSP] = DWARF_REG_LOC(DWARF_X86_RSP);
|
|
c->loc[DWARF_X86_R8] = DWARF_REG_LOC(DWARF_X86_R8);
|
|
c->loc[DWARF_X86_R9] = DWARF_REG_LOC(DWARF_X86_R9);
|
|
c->loc[DWARF_X86_R10] = DWARF_REG_LOC(DWARF_X86_R10);
|
|
c->loc[DWARF_X86_R11] = DWARF_REG_LOC(DWARF_X86_R11);
|
|
c->loc[DWARF_X86_R12] = DWARF_REG_LOC(DWARF_X86_R12);
|
|
c->loc[DWARF_X86_R13] = DWARF_REG_LOC(DWARF_X86_R13);
|
|
c->loc[DWARF_X86_R14] = DWARF_REG_LOC(DWARF_X86_R14);
|
|
c->loc[DWARF_X86_R15] = DWARF_REG_LOC(DWARF_X86_R15);
|
|
c->loc[DWARF_X86_RIP] = DWARF_REG_LOC(DWARF_X86_RIP);
|
|
|
|
c->ip = fetch_reg(c->task, RIP);
|
|
c->cfa = fetch_reg(c->task, RSP);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
c->loc[DWARF_X86_EAX] = DWARF_REG_LOC(DWARF_X86_EAX);
|
|
c->loc[DWARF_X86_ECX] = DWARF_REG_LOC(DWARF_X86_ECX);
|
|
c->loc[DWARF_X86_EDX] = DWARF_REG_LOC(DWARF_X86_EDX);
|
|
c->loc[DWARF_X86_EBX] = DWARF_REG_LOC(DWARF_X86_EBX);
|
|
c->loc[DWARF_X86_ESP] = DWARF_REG_LOC(DWARF_X86_ESP);
|
|
c->loc[DWARF_X86_EBP] = DWARF_REG_LOC(DWARF_X86_EBP);
|
|
c->loc[DWARF_X86_ESI] = DWARF_REG_LOC(DWARF_X86_ESI);
|
|
c->loc[DWARF_X86_EDI] = DWARF_REG_LOC(DWARF_X86_EDI);
|
|
c->loc[DWARF_X86_EIP] = DWARF_REG_LOC(DWARF_X86_EIP);
|
|
|
|
c->ip = fetch_reg(c->task, dwarf_to_regnum_map32[DWARF_X86_EIP]);
|
|
c->cfa = fetch_reg(c->task, dwarf_to_regnum_map32[DWARF_X86_ESP]);
|
|
}
|
|
|
|
c->use_prev_instr = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int dwarf_arch_step(struct dwarf_addr_space *as)
|
|
{
|
|
unsigned int i;
|
|
arch_addr_t prev_cfa;
|
|
struct dwarf_cursor *c = &as->cursor;
|
|
const struct arch_reg *arch_reg;
|
|
int ret;
|
|
|
|
#ifdef __x86_64__
|
|
arch_reg = as->is_64bit ? &arch_reg64 : &arch_reg32;
|
|
#else
|
|
arch_reg = &arch_reg32;
|
|
#endif
|
|
|
|
if (is_signal_frame(c))
|
|
return -DWARF_EBADFRAME;
|
|
|
|
if (DWARF_IS_NULL_LOC(c->loc[arch_reg->bp])) {
|
|
c->ip = 0;
|
|
return 0;
|
|
}
|
|
|
|
prev_cfa = c->cfa;
|
|
|
|
if (is_plt_entry(as)) {
|
|
debug(DEBUG_FUNCTION, "found plt entry");
|
|
|
|
/* Like regular frame, CFA = SP+addrsz, RA = [CFA-addrsz], no regs saved. */
|
|
c->loc[arch_reg->ip] = DWARF_MEM_LOC(c->cfa);
|
|
c->cfa += DWARF_ADDR_SIZE(as);
|
|
}
|
|
else {
|
|
struct dwarf_loc rbp_loc = c->loc[arch_reg->bp];
|
|
|
|
/* Mark all registers unsaved */
|
|
for (i = 0; i < as->num_regs; ++i)
|
|
c->loc[i] = DWARF_NULL_LOC;
|
|
|
|
arch_addr_t rbp;
|
|
|
|
ret = dwarf_get(as, rbp_loc, &rbp);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (rbp > c->cfa && rbp - c->cfa <= 128 * 1024) {
|
|
c->loc[arch_reg->bp] = DWARF_MEM_LOC(rbp);
|
|
c->loc[arch_reg->ip] = DWARF_MEM_LOC(rbp + DWARF_ADDR_SIZE(as));
|
|
c->cfa = rbp + DWARF_ADDR_SIZE(as) * 2;
|
|
c->use_prev_instr = 0;
|
|
}
|
|
}
|
|
|
|
if (c->cfa == prev_cfa)
|
|
return -DWARF_EBADFRAME;
|
|
|
|
c->ret_addr_column = arch_reg->ip;
|
|
|
|
if (!DWARF_IS_NULL_LOC(c->loc[arch_reg->ip])) {
|
|
ret = dwarf_get(as, c->loc[arch_reg->ip], &c->ip);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
debug(DEBUG_FUNCTION, "Frame Chain [IP=%#lx] = %#lx", DWARF_GET_LOC(c->loc[arch_reg->ip]), c->ip);
|
|
}
|
|
else
|
|
c->ip = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int dwarf_arch_map_reg(struct dwarf_addr_space *as, unsigned int reg)
|
|
{
|
|
#ifdef __x86_64__
|
|
if (as->is_64bit) {
|
|
if (reg >= ARRAY_SIZE(dwarf_to_regnum_map64))
|
|
return -DWARF_EBADREG;
|
|
|
|
return dwarf_to_regnum_map64[reg];
|
|
}
|
|
#endif
|
|
if (reg >= ARRAY_SIZE(dwarf_to_regnum_map32))
|
|
return -DWARF_EBADREG;
|
|
|
|
return dwarf_to_regnum_map32[reg];
|
|
}
|
|
|
|
static struct call_op {
|
|
unsigned int off;
|
|
unsigned int len;
|
|
unsigned char *mask;
|
|
unsigned char *op;
|
|
} call_op[] =
|
|
{
|
|
{ 5, 1, (unsigned char [1]){ 0xff }, (unsigned char [1]){ 0xe8 } }, /* call */
|
|
{ 3, 2, (unsigned char [2]){ 0xff, 0xff }, (unsigned char [2]){ 0xff, 0x14 } }, /* call *(sp) */
|
|
{ 2, 2, (unsigned char [2]){ 0xff, 0xfc }, (unsigned char [2]){ 0xff, 0x10 } }, /* call *(reg) */
|
|
{ 4, 2, (unsigned char [2]){ 0xff, 0xff }, (unsigned char [2]){ 0xff, 0x54 } }, /* call *off8(reg, idx, scale) */
|
|
{ 3, 2, (unsigned char [2]){ 0xff, 0xfc }, (unsigned char [2]){ 0xff, 0x50 } }, /* call *off8 */
|
|
{ 7, 2, (unsigned char [2]){ 0xff, 0xff }, (unsigned char [2]){ 0xff, 0x94 } }, /* call *off32(reg, idx, scale) */
|
|
{ 6, 2, (unsigned char [2]){ 0xff, 0xfc }, (unsigned char [2]){ 0xff, 0x90 } }, /* call *off32 */
|
|
{ 2, 2, (unsigned char [2]){ 0xff, 0xfc }, (unsigned char [2]){ 0xff, 0xd0 } }, /* call *reg */
|
|
{ 0 }
|
|
};
|
|
|
|
int dwarf_arch_check_call(struct dwarf_addr_space *as, arch_addr_t ip)
|
|
{
|
|
static struct call_op *p;
|
|
struct dwarf_cursor *c = &as->cursor;
|
|
struct libref *libref = c->libref;
|
|
|
|
for(p = call_op; p->len; ++p) {
|
|
if (ip - ARCH_ADDR_T(libref->load_addr) >= p->off) {
|
|
unsigned int i;
|
|
unsigned char *addr = libref->image_addr + ip - p->off - libref->load_addr;
|
|
|
|
for(i = 0; i < call_op[i].len; ++i) {
|
|
if ((addr[i] & p->mask[i]) != p->op[i])
|
|
break;
|
|
}
|
|
|
|
if (i == p->len)
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|