首页 > 系统相关 >转:重磅原创)冬之焱: 谈谈Linux内核的栈回溯与妙用

转:重磅原创)冬之焱: 谈谈Linux内核的栈回溯与妙用

时间:2024-06-20 14:11:37浏览次数:13  
标签:__ unwind idx ctrl Linux unsigned long 回溯 冬之焱

 unwind.c

// SPDX-License-Identifier: GPL-2.0-only
/*
 * arch/arm/kernel/unwind.c
 *
 * Copyright (C) 2008 ARM Limited
 *
 * Stack unwinding support for ARM
 *
 * An ARM EABI version of gcc is required to generate the unwind
 * tables. For information about the structure of the unwind tables,
 * see "Exception Handling ABI for the ARM Architecture" at:
 *
 * http://infocenter.arm.com/help/topic/com.arm.doc.subset.swdev.abi/index.html
 */

#include <stdio.h>
#include <stdint.h>
#include "unwind.h"


// /* Dummy functions to avoid linker complaints */
// void __aeabi_unwind_cpp_pr0(void)
// {
// };
// EXPORT_SYMBOL(__aeabi_unwind_cpp_pr0);

// void __aeabi_unwind_cpp_pr1(void)
// {
// };
// EXPORT_SYMBOL(__aeabi_unwind_cpp_pr1);

// void __aeabi_unwind_cpp_pr2(void)
// {
// };
// EXPORT_SYMBOL(__aeabi_unwind_cpp_pr2);

#ifdef CONFIG_THUMB2_KERNEL
#define frame_pointer(regs) (regs)->ARM_r7
#else
#define frame_pointer(regs) (regs)->ARM_fp
#endif

#define ALIGN_UN(x, a)    (((x) + (a) - 1) & ~((a) - 1))


static __always_inline
void arm_get_current_stackframe(struct pt_regs *regs, struct stackframe *frame)
{
        frame->fp = frame_pointer(regs);
        frame->sp = regs->ARM_sp;
        frame->lr = regs->ARM_lr;
        frame->pc = regs->ARM_pc;
}


extern const struct unwind_idx __start_unwind_idx[];
static const struct unwind_idx *__origin_unwind_idx;
extern const struct unwind_idx __stop_unwind_idx[];

/* Convert a prel31 symbol to an absolute address */
#define prel31_to_addr(ptr)                \
({                            \
    /* sign-extend to 32 bits */            \
    long offset = (((long)*(ptr)) << 1) >> 1;    \
    (unsigned long)(ptr) + offset;            \
})


void dump_backtrace_entry(unsigned long where, unsigned long from,
              unsigned long frame)
{
    unsigned long end = frame + 4 + sizeof(struct pt_regs);
    rt_kprintf("Function entered at [<%08lx>] from [<%08lx>]\n",
         where, from);
}




/*
 * Binary search in the unwind index. The entries are
 * guaranteed to be sorted in ascending order by the linker.
 *
 * start = first entry
 * origin = first entry with positive offset (or stop if there is no such entry)
 * stop - 1 = last entry
 */
static const struct unwind_idx *search_index(unsigned long addr,
                       const struct unwind_idx *start,
                       const struct unwind_idx *origin,
                       const struct unwind_idx *stop)
{
    unsigned long addr_prel31;

    pr_debug("%s(%08lx, %p, %p, %p)\n",
            __func__, addr, start, origin, stop);

    /*
     * only search in the section with the matching sign. This way the
     * prel31 numbers can be compared as unsigned longs.
     */
    if (addr < (unsigned long)start)
        /* negative offsets: [start; origin) */
        stop = origin;
    else
        /* positive offsets: [origin; stop) */
        start = origin;

    /* prel31 for address relavive to start */
    addr_prel31 = (addr - (unsigned long)start) & 0x7fffffff;

    while (start < stop - 1) {
        const struct unwind_idx *mid = start + ((stop - start) >> 1);

        /*
         * As addr_prel31 is relative to start an offset is needed to
         * make it relative to mid.
         */
        if (addr_prel31 - ((unsigned long)mid - (unsigned long)start) <
                mid->addr_offset)
            stop = mid;
        else {
            /* keep addr_prel31 relative to start */
            addr_prel31 -= ((unsigned long)mid -
                    (unsigned long)start);
            start = mid;
        }
    }

    if ((start->addr_offset <= addr_prel31))
        return start;
    else {
        pr_warn("unwind: Unknown symbol address %08lx\n", addr);
        return NULL;
    }
}

static const struct unwind_idx *unwind_find_origin(
        const struct unwind_idx *start, const struct unwind_idx *stop)
{
    pr_debug("%s(%p, %p)\n", __func__, start, stop);
    while (start < stop) {
        const struct unwind_idx *mid = start + ((stop - start) >> 1);

        if (mid->addr_offset >= 0x40000000)
            /* negative offset */
            start = mid + 1;
        else
            /* positive offset */
            stop = mid;
    }
    pr_debug("%s -> %p\n", __func__, stop);
    return stop;
}

static const struct unwind_idx *unwind_find_idx(unsigned long addr)
{
    const struct unwind_idx *idx = NULL;
    unsigned long flags;

    pr_debug("%s(%08lx)\n", __func__, addr);

    __origin_unwind_idx =
        unwind_find_origin(__start_unwind_idx,
                __stop_unwind_idx);

    /* main unwind table */
    idx = search_index(addr, __start_unwind_idx,
                __origin_unwind_idx,
                __stop_unwind_idx);


    pr_debug("%s: idx = %p\n", __func__, idx);
    return idx;
}

static unsigned long unwind_get_byte(struct unwind_ctrl_block *ctrl)
{
    unsigned long ret;

    if (ctrl->entries <= 0) {
        pr_warn("unwind: Corrupt unwind table\n");
        return 0;
    }

    ret = (*ctrl->insn >> (ctrl->byte * 8)) & 0xff;

    if (ctrl->byte == 0) {
        ctrl->insn++;
        ctrl->entries--;
        ctrl->byte = 3;
    } else
        ctrl->byte--;

    return ret;
}

/* Before poping a register check whether it is feasible or not */
static int unwind_pop_register(struct unwind_ctrl_block *ctrl,
                unsigned long **vsp, unsigned int reg)
{
    if ((ctrl->check_each_pop))
        if (*vsp >= (unsigned long *)ctrl->sp_high)
            return -URC_FAILURE;

    rt_kprintf("%p: %p %p\n", ctrl->vrs[SP] , *vsp , *(*vsp));
    ctrl->vrs[reg] = *(*vsp)++;
    rt_kprintf("%p: %p %p , %d %p\n", vsp , *vsp, **vsp ,reg ,ctrl->vrs[reg]);
    return URC_OK;
}

/* Helper functions to execute the instructions */
static int unwind_exec_pop_subset_r4_to_r13(struct unwind_ctrl_block *ctrl,
                        unsigned long mask)
{
    unsigned long *vsp = (unsigned long *)ctrl->vrs[SP];
    int load_sp, reg = 4;

    load_sp = mask & (1 << (13 - 4));
    while (mask) {
        if (mask & 1)
            if (unwind_pop_register(ctrl, &vsp, reg))
                return -URC_FAILURE;
        mask >>= 1;
        reg++;
    }
    if (!load_sp)
        ctrl->vrs[SP] = (unsigned long)vsp;

    return URC_OK;
}

static int unwind_exec_pop_r4_to_rN(struct unwind_ctrl_block *ctrl,
                    unsigned long insn)
{
    unsigned long *vsp = (unsigned long *)ctrl->vrs[SP];
    int reg;

    /* pop R4-R[4+bbb] */
    for (reg = 4; reg <= 4 + (insn & 7); reg++)
        if (unwind_pop_register(ctrl, &vsp, reg))
                return -URC_FAILURE;

    if (insn & 0x8)
        if (unwind_pop_register(ctrl, &vsp, 14))
                return -URC_FAILURE;

    ctrl->vrs[SP] = (unsigned long)vsp;

    return URC_OK;
}

static int unwind_exec_pop_subset_r0_to_r3(struct unwind_ctrl_block *ctrl,
                        unsigned long mask)
{
    unsigned long *vsp = (unsigned long *)ctrl->vrs[SP];
    int reg = 0;

    /* pop R0-R3 according to mask */
    while (mask) {
        if (mask & 1)
            if (unwind_pop_register(ctrl, &vsp, reg))
                return -URC_FAILURE;
        mask >>= 1;
        reg++;
    }
    ctrl->vrs[SP] = (unsigned long)vsp;

    return URC_OK;
}

/*
 * Execute the current unwind instruction.
 */
static int unwind_exec_insn(struct unwind_ctrl_block *ctrl)
{
    unsigned long insn = unwind_get_byte(ctrl);
    int ret = URC_OK;

    pr_debug("%s: insn = %08lx\n", __func__, insn);

    if ((insn & 0xc0) == 0x00)
        ctrl->vrs[SP] += ((insn & 0x3f) << 2) + 4;
    else if ((insn & 0xc0) == 0x40)
        ctrl->vrs[SP] -= ((insn & 0x3f) << 2) + 4;
    else if ((insn & 0xf0) == 0x80) 
    {
        unsigned long mask;
        insn = (insn << 8) | unwind_get_byte(ctrl);
        mask = insn & 0x0fff;
        if (mask == 0) 
        {
            pr_warn("unwind: 'Refuse to unwind' instruction %04lx\n",
                insn);
            return -URC_FAILURE;
        }
        ret = unwind_exec_pop_subset_r4_to_r13(ctrl, mask);
        if (ret)
            goto error;
    } 
    else if ((insn & 0xf0) == 0x90 &&
           (insn & 0x0d) != 0x0d)
        ctrl->vrs[SP] = ctrl->vrs[insn & 0x0f];
    else if ((insn & 0xf0) == 0xa0) 
    {
        ret = unwind_exec_pop_r4_to_rN(ctrl, insn);
        if (ret)
            goto error;
    } 
    else if (insn == 0xb0) 
    {
        if (ctrl->vrs[PC] == 0)
            ctrl->vrs[PC] = ctrl->vrs[LR];
        /* no further processing */
        ctrl->entries = 0;
    } 
    else if (insn == 0xb1) 
    {
        unsigned long mask = unwind_get_byte(ctrl);

        if (mask == 0 || mask & 0xf0)
        {
            pr_warn("unwind: Spare encoding %04lx\n",
                (insn << 8) | mask);
            return -URC_FAILURE;
        }

        ret = unwind_exec_pop_subset_r0_to_r3(ctrl, mask);
        if (ret)
            goto error;
    } 
    else if (insn == 0xb2) 
    {
        unsigned long uleb128 = unwind_get_byte(ctrl);

        ctrl->vrs[SP] += 0x204 + (uleb128 << 2);
    } 
    else
    {
        pr_warn("unwind: Unhandled instruction %02lx\n", insn);
        return -URC_FAILURE;
    }

    pr_debug("%s: fp = %08lx sp = %08lx lr = %08lx pc = %08lx\n", __func__,
         ctrl->vrs[FP], ctrl->vrs[SP], ctrl->vrs[LR], ctrl->vrs[PC]);

error:
    return ret;
}

/*
 * Unwind a single frame starting with *sp for the symbol at *pc. It
 * updates the *pc and *sp with the new values.
 */
int unwind_frame(struct stackframe *frame)
{
    unsigned long low;
    const struct unwind_idx *idx;
    struct unwind_ctrl_block ctrl;
    rt_thread_t current_thread = rt_thread_self();

    /* store the highest address on the stack to avoid crossing it*/
    low = frame->sp;
    ctrl.sp_high = ALIGN_UN(low, current_thread->stack_size);

    pr_debug("%s(pc = %08lx lr = %08lx sp = %08lx)\n", __func__,
         frame->pc, frame->lr, frame->sp);

    idx = unwind_find_idx(frame->pc);
    if (!idx) {
        pr_warn("unwind: Index not found %08lx\n", frame->pc);
        return -URC_FAILURE;
    }

    ctrl.vrs[FP] = frame->fp;
    ctrl.vrs[SP] = frame->sp;
    ctrl.vrs[LR] = frame->lr;
    ctrl.vrs[PC] = 0;

    if (idx->insn == 1)
        /* can't unwind */
        return -URC_FAILURE;
    else if ((idx->insn & 0x80000000) == 0)
        /* prel31 to the unwind table */
        ctrl.insn = (unsigned long *)prel31_to_addr(&idx->insn);
    else if ((idx->insn & 0xff000000) == 0x80000000)
        /* only personality routine 0 supported in the index */
        ctrl.insn = &idx->insn;
    else {
        pr_warn("unwind: Unsupported personality routine %08lx in the index at %p\n",
            idx->insn, idx);
        return -URC_FAILURE;
    }

    /* check the personality routine */
    if ((*ctrl.insn & 0xff000000) == 0x80000000) {
        ctrl.byte = 2;
        ctrl.entries = 1;
    } else if ((*ctrl.insn & 0xff000000) == 0x81000000) {
        ctrl.byte = 1;
        ctrl.entries = 1 + ((*ctrl.insn & 0x00ff0000) >> 16);
    } else {
        pr_warn("unwind: Unsupported personality routine %08lx at %p\n",
            *ctrl.insn, ctrl.insn);
        return -URC_FAILURE;
    }

    ctrl.check_each_pop = 0;

    while (ctrl.entries > 0) {
        int urc;
        if ((ctrl.sp_high - ctrl.vrs[SP]) < sizeof(ctrl.vrs))
            ctrl.check_each_pop = 1;
        urc = unwind_exec_insn(&ctrl);
        if (urc < 0)
            return urc;
        if (ctrl.vrs[SP] < low || ctrl.vrs[SP] >= ctrl.sp_high)
            return -URC_FAILURE;
    }

    if (ctrl.vrs[PC] == 0)
        ctrl.vrs[PC] = ctrl.vrs[LR];

    /* check for infinite loop */
    if (frame->pc == ctrl.vrs[PC] && frame->sp == ctrl.vrs[SP])
        return -URC_FAILURE;

    frame->fp = ctrl.vrs[FP];
    frame->sp = ctrl.vrs[SP];
    frame->lr = ctrl.vrs[LR];
    frame->pc = ctrl.vrs[PC];

    return URC_OK;
}

void unwind_backtrace(struct pt_regs *regs)
{
    struct stackframe frame;

    pr_debug("%s(regs = %p)\n", __func__, regs);


    if (regs) {
        arm_get_current_stackframe(regs, &frame);
        /* PC might be corrupted, use LR in that case. */
        // if (!kernel_text_address(regs->ARM_pc))
        //     frame.pc = regs->ARM_lr;
    } 
    else
    {
        rt_kprintf("resg is  null ,%s",__func__);
    }
    while (1) {
        int urc;
        unsigned long where = frame.pc;

        urc = unwind_frame(&frame);
        if (urc < 0)
            break;
        dump_backtrace_entry(where, frame.pc, frame.sp - 4);
    }
}

// struct unwind_table *unwind_table_add(unsigned long start, unsigned long size,
//                       unsigned long text_addr,
//                       unsigned long text_size)
// {
//     unsigned long flags;
//     struct unwind_table *tab = malloc(sizeof(*tab));

//     pr_debug("%s(%08lx, %08lx, %08lx, %08lx)\n", __func__, start, size,
//          text_addr, text_size);

//     if (!tab)
//         return tab;

//     tab->start = (const struct unwind_idx *)start;
//     tab->stop = (const struct unwind_idx *)(start + size);
//     tab->origin = unwind_find_origin(tab->start, tab->stop);
//     tab->begin_addr = text_addr;
//     tab->end_addr = text_addr + text_size;
//     list_add_tail(&tab->list, &unwind_tables);
//     return tab;
// }

// void unwind_table_del(struct unwind_table *tab)
// {
//     unsigned long flags;

//     if (!tab)
//         return;
//     list_del(&tab->list);
//     free(tab);
// }

unwind.h

/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * arch/arm/include/asm/unwind.h
 *
 * Copyright (C) 2008 ARM Limited
 */

#ifndef __ASM_UNWIND_H
#define __ASM_UNWIND_H

#include <rtthread.h>

#ifndef __ASSEMBLY__

/* Unwind reason code according the the ARM EABI documents */
enum unwind_reason_code {
    URC_OK = 0,            /* operation completed successfully */
    URC_CONTINUE_UNWIND = 8,
    URC_FAILURE = 9            /* unspecified failure of some kind */
};

struct unwind_idx {
    unsigned long addr_offset;
    unsigned long insn;
};

struct stackframe {
    /*
     * FP member should hold R7 when CONFIG_THUMB2_KERNEL is enabled
     * and R11 otherwise.
     */
    unsigned long fp;
    unsigned long sp;
    unsigned long lr;
    unsigned long pc;
};

struct unwind_ctrl_block {
    unsigned long vrs[16];        /* virtual register set */
    const unsigned long *insn;    /* pointer to the current instructions word */
    unsigned long sp_high;        /* highest value of sp allowed */
    /*
     * 1 : check for stack overflow for each register pop.
     * 0 : save overhead if there is plenty of stack remaining.
     */
    int check_each_pop;
    int entries;            /* number of entries left to interpret */
    int byte;            /* current byte number in the instructions word */
};

enum regs {
#ifdef CONFIG_THUMB2_KERNEL
    FP = 7,
#else
    FP = 11,
#endif
    SP = 13,
    LR = 14,
    PC = 15
};


#define pr_debug(...) rt_kprintf(__VA_ARGS__)
#define pr_warn(...) rt_kprintf(__VA_ARGS__)

struct pt_regs {
    unsigned long uregs[17];
};
#define ARM_cpsr    uregs[16]
#define ARM_pc        uregs[15]
#define ARM_lr        uregs[14]
#define ARM_sp        uregs[13]
#define ARM_ip        uregs[12]
#define ARM_fp        uregs[11]
#define ARM_r10        uregs[10]
#define ARM_r9        uregs[9]
#define ARM_r8        uregs[8]
#define ARM_r7        uregs[7]
#define ARM_r6        uregs[6]
#define ARM_r5        uregs[5]
#define ARM_r4        uregs[4]
#define ARM_r3        uregs[3]
#define ARM_r2        uregs[2]
#define ARM_r1        uregs[1]
#define ARM_r0        uregs[0]



// struct unwind_table {
//     struct list_head list;
//     const struct unwind_idx *start;
//     const struct unwind_idx *origin;
//     const struct unwind_idx *stop;
//     unsigned long begin_addr;
//     unsigned long end_addr;
// };

// extern struct unwind_table *unwind_table_add(unsigned long start,
//                          unsigned long size,
//                          unsigned long text_addr,
//                          unsigned long text_size);
// extern void unwind_table_del(struct unwind_table *tab);
extern void unwind_backtrace(struct pt_regs *regs);

#endif    /* !__ASSEMBLY__ */

#ifdef CONFIG_ARM_UNWIND
#define UNWIND(code...)        code
#else
#define UNWIND(code...)
#endif

#endif    /* __ASM_UNWIND_H */

 

 

 

1 当内核某处陷入死循环,有时运行sysrq的内核线程栈回溯功能可以排查,但并不适用所用情况,笔者实际项目遇到过。最后是在系统定时钟中断函数,对死循环线程栈回溯20多级终于找到死循环的函数。

2 当应用程序段错误,内核捕捉到崩溃,对崩溃的应用空间进程/线程栈回溯,像内核栈回溯一样,打印应用段错误进程/线程的层层函数调用关系。虽然运用core文件分析或者gdb也很简便排查应用崩溃问题,但是对于不容易复现、测试部偶先的、客户现场偶先的,这二者就很难发挥作用。还有就是如果崩溃发生在C库中,CPU的pc和lr(arm架构)寄存器指向的函数指令在C库的用户空间,很难找到应用的代码哪里调用了C库的函数。arm架构网上能找到应用层栈回溯的例子,但是编译较麻烦,代码并不容易理解,况且mips能在应用层实现吗?还是在内核实现应用程序栈回溯比较方便。

3 应用程序发生double free,运用内核的栈回溯功能,找到应用代码哪里发生了double free。double free是C库层发现并截获该事件,然后向当前进程/线程发送SIGABRT进程终止信号,后续就是内核强制清理该进程/线程。double free比应用程序段错误更麻烦,后者内核还会打印出错进程/线程名字、pid、pc和lr寄存器值,double free这些打印全没有。笔者做过的一个项目,发布前,遇到一例double free崩溃问题,极难复现,当初要是把double free内核对出问题进程/线程栈回溯的功能做进内核,就能找到出问题的应用函数了。

4 当应用程序出现锁死问题,对应用所有线程栈回溯,分析每个线程的函数执行流程,对查找锁死问题有帮助。

 

2 栈回溯的原理解释

2.1 基于fp栈帧寄存器形式的栈回溯

 

2.2 unwind 形式的栈回溯

这个unwind段中存储着跟函数入栈相关的关键数据。当函数执行入栈指令后,在unwind段会保存跟入栈指令一一对应的编码数据,

当函数执行入栈指令后,在unwind段会保存跟入栈指令一一对应的编码数据,根据这些编码数据,就能计算出当前函数栈大小和cpu的哪些寄存器入栈了,在栈中什么位置。当栈回溯时,首先根据当前函数中的指令地址,就可以计算出函数unwind段的地址,然后从unwind段取出跟入栈有关的编码数据,根据这些编码数据就能计算出当前函数栈的大小以及入栈时lr寄存器数据在栈中的存储地址。这样就可以找到lr寄存器数据,就是当前函数返回地址,也就是上一级函数的指令地址。此时sp一般指向的函数栈顶,sp+函数栈大小就是上一级函数的栈顶。这样就完成了一次栈回溯,并且知道了上一级函数的指令地址和栈顶地址,按照同样的方法就能对上一级函数栈回溯,类推就能实现整个栈回溯流程。为了方便理解,下方举一个实际调试的示例。该示例中首先列出栈回溯过程每个函数unwind段的编码数据和栈数据。

读者想研究清楚的话,可以阅读内核arm架构unwind_frame函数实现流程,其中最核心的是在unwind_exec_insn函数,根据0xa8,0xb0这些跟函数入栈过程有关的编码数据,分析入栈过程的详细信息,计算出函数lr寄存器保存在栈中的地址和上一级函数的栈顶地址。

 

 

这就表示match_dev_by_uuid函数在unwind段编码数据是0x808ab0b0,0xc0008af8是该函数指令首地址。其中有用的是0xa8 ,表示pop {r4,r14}出栈指令,0xb0表示unwind段结束。

 

2.3 fp和unwind形式栈回溯的比较

上文介绍了两种常用的栈回溯形式的基本原理,并辅助了例子说明。基于fp寄存器的栈回溯和unwind形式的栈回溯,各有优点和缺点。fp形式的栈回溯,基于APCS规范,入栈过程必须要将pc、lr、fp等4个寄存器入栈(其实没必要这样做,只需把lr和fp入栈),并且消耗的入栈指令要多(除了入栈pc、lr、fp等4个寄存器,还得将栈底地址保存到fp),同时还浪费了寄存器,至少fp寄存器是浪费了,不能参与指令数据运算,CPU寄存器是很宝贵的,多一个对加快指令数据运算是有积极意义的。而unwind形式的栈回溯,就没有这些缺点,仅仅只是将入栈相关的指令的编码保存到unwind段中,不用把无关的寄存器保存到栈中,也不用浪费fp寄存器。unwind形式栈回溯是有缺点的,首先栈回溯的速度肯定比fp形式栈回溯慢,理解难度要比fp形式大很多,并且,站在开发者角度,使用前还得对每个入栈指令编码,这都是需要工作量的。但是站在使用者角度,这些缺点影响并不大,所以现在有很多arm32系统用的是unwind形式的栈回溯。

 

3 linux内核栈回溯的原理

当内核崩溃,将会执行异常处理程序,这里以mips架构为例,崩溃函数执行流程是:

do_page_fault()->die()->show_registers()->show_stacktrace()->show_backtrace()

栈回溯的过程就是在show_backtrace()函数,arm架构最终是在dump_backtrace()函数,内核崩溃处理流程与mips不同。arm架构栈回溯过程相对来说更简单,首先讲解arm架构的栈回溯过程。

不同内核版本,内核代码有差异,本内核版本3.10.104

3.1.1 内核源码分析

如果读者对上一节的演示理解的话,理解下方的源码就比较容易。

内核崩溃时,产生异常,内核的异常处理程序自动将崩溃时的CPU寄存器存入struct pt_regs结构体,并传入该函数,相关代码不再列出。这样栈回溯的关键环节就是红色标注的代码,先对frame.fp,frame.sp,frame.pc赋值。下方进入while循环,先执行unwind_frame(&frame) 找出崩溃过程的每个函数中的汇编指令地址,存入frame.pc(第一次while循环是直接where = frame.pc赋值,这就是当前崩溃函数的崩溃指令地址),下次循环存入where变量,再传入dump_backtrace_entry函数,在该函数中打印诸如[<c016c873>] chrdev_open+0x12/0x4B1 的字符串。

 

arch/arm64/kernel/stacktrace.c

 

4 linux内核栈回溯的应用

文章最开头说过,笔者在实际项目开发过程,已经总结出了3个内核栈回溯的应用:

1 应用程序崩溃,像内核栈回溯一样打印整个崩溃过程,应用函数的调用关系

2 应用程序发生double free,像内核栈回溯一样打印double free过程,应用函数的调用关系

3 内核陷入死循环,sysrq的内核线程栈回溯功能无法发挥作用时,在系统定时钟中断函数中对卡死线程栈回溯,找出卡死位置

下文逐一讲解。

4.1 应用程序崩溃栈回溯

笔者在研究过内核栈回溯功能后,不禁发问,为什么不能用同样的方法对应用程序的崩溃栈回溯呢?不管是内核空间,应用空间,程序的指令是一样的,无非是地址有差异,函数入栈出栈原理是一样的。栈回溯的入口,arm架构是获取崩溃线程/进程的pc、fp、lr寄存器值,mips架构是获取pc、ra、sp寄存器值,有了这些值就能按照各自的回溯规律,实现栈回溯。从理论上来说,完全是可以实现的。

4.1 .1 arm架构应用程序栈回溯的实现

当应用程序发生崩溃,与内核一样,系统自动将崩溃时所有的CPU寄存器存入struct pt_regs结构,一般崩溃入口函数是do_page_fault,又因为是应用程序崩溃,所以是__do_user_fault函数,这里直接分析__do_user_fault。

 unwind_idx 和 eh_frame   5. rt-thread下的移植 将linux unwind.c unwind.h 关于内核参数解释的所有代码注释掉,跟tsk有关的也注释掉       rtconfig.py 编译参数加funwind-tables     DEVICE = ' -Wall -mcpu=cortex-a9 -mfpu=vfpv3 -ftree-vectorize -mfloat-abi=softfp -ffunction-sections -funwind-tables' ld文件 增加unwind段

. = ALIGN(4);
.ARM.unwind_idx : {
__start_unwind_idx = .;
*(.ARM.exidx*)
__stop_unwind_idx = .;
}
.ARM.unwind_tab : {
__start_unwind_tab = .;
*(.ARM.extab*)
__stop_unwind_tab = .;
}

.ARM.exidx : {
__exidx_start = .;
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
__exidx_end = .;
}

插入 trap.c 异常处理函数中  

 

 

标签:__,unwind,idx,ctrl,Linux,unsigned,long,回溯,冬之焱
From: https://www.cnblogs.com/ycjstudy/p/18253670

相关文章

  • linux常见特殊符号介绍
    ${}、$[]、$()的区别${}Shell中使用一个已经定义过的变量,只要在变量名前面加美元符号$即可。变量名外面的{}是可选的,只是为了帮助解释器识别变量的边界。str="java"echo"Iam$str"echo"Iam$s{str}Script"$[]是运算操作符,用于对整数进行数学运算echo$[3*5]#......
  • Linux 安装mongodb
    1.1Mongodb要求使用最新稳定版本安装包下载地址:https://www.mongodb.com/try/download/community本次选择:mongodb-linux-x86_64-rhel70-4.4.13.tgz1.2安装步骤上传安装包到服务器,并解压#tar–zxvfmongodb-linux-x86_64-rhel70-4.4.13.tgz 重命名解压后的文件名#mvmo......
  • Linux 下普通用户不通过 sudo 使用 docker 命令
    Linux环境下普通用户没有执行docker命令的权限是因为Docker的安全设计。Docker守护进程(daemon)默认以root用户身份运行,因为它需要访问系统的底层资源来管理容器。出于安全考虑,普通用户(非root用户)不应该能够直接执行可能具有破坏性的Docker命令。但是,Docker提供了一种......
  • [Linux Mint]截屏
    造冰箱的大熊猫@cnblogs2024/6/20,LinuxMint1、快捷键PrtScr:按下PrintScreen按键,对整个桌面(屏幕)截屏,并存入文件Alt+PrtScr:对当前窗口截屏Shift+PrtScr:对矩形区域截屏Ctrl+PrtScr:对整个桌面(屏幕)截屏,存入剪切板Ctrl+Alt+PrtScr:对当前窗口截屏Ctrl+Shift+Pr......
  • linux - 字符串替换
    使用场景:部署项目的时候,需要统一修改IP地址等内容。缺点:这些命令,都缺少必要的校验功能,容易因为操作失误,会出现未替换,或者替换成空串的情况。比如说:写了好多行的sed命令,不小心删了一行代码,这种情况下,执行代码不会报错,因此很容易埋下安全隐患。推荐:要进行很复杂的替换时,还是......
  • (转)Linux环境下使用logrotate工具实现nginx日志切割
    原文:https://www.cnblogs.com/even160941/p/13903291.html一.前提背景及需求Nginx运行日志默认保存在Nginx安装目录下的 /usr/local/nginx/logs目录(或/var/log/nginx目录下),包含access.log和error.log两个文件。(1) access.log 记录了哪些用户、哪些页面以及用户浏览器、i......
  • linux - curl
    curl(commandlineuniformresourcelocator),URL命令行,用于发送http请求场景:项目运维过程中,如果没有postman工具,会用到这些命令;docker容器内,如果没有开放端口,只能进容器内用curl进行测试了。#不带有任何参数时,curl默认发出GET请求。curlhttps://www.baidu.com......
  • linux - nfs挂载
    NFS(全称NetworkFileSystem),即网络文件系统。通过网络,让不同的机器、不同的操作系统可以共享彼此的文件。一般的挂载,是买一个新硬盘,然后挂载到我们的系统,而NFS挂载,是把其它主机的文件夹,挂载到我们的系统,完成NFS挂载之后,操作其它主机的文件,就像操作本机的文件一样。效果:理解......
  • linux - grep
    系统运维中,查看日志时,最经常使用的就是grep、cat和tail三个命令。grep(globalregularexpression)命令,用于查找文件中符合条件的字符串基本语法:grep[选项][文件]参数选项:-i:忽略大小写进行匹配。-v:反向查找,只打印不匹配的行。-n:显示匹配行的行号。-r:递归查找子目......
  • linux - cat
    系统运维中,查看日志时,最经常使用的就是grep、cat和tail三个命令。cat(英文全拼:concatenate)命令,用于连接文件并打印到标准输出设备上。命令语法:cat[选项][文件]参数选项:-n:显示行号;-b:显示非空行号;-s:合并空白行;-E:在每行结尾添加$符号;-T:将制表符显示为^I,tab键......