kernel BUG_ON macro的實現以及brk指令觸發異常后的異常處理callstack


kernel BUG_ON macro的實現以及brk指令觸發異常后的異常處理callstack

kernel里的兩個macro

BUG_ON(condition),如果condition條件滿足,判斷為真,則會造成一個debug exception;

BUG(),這個沒有條件判斷,調用它則會直接造成一個debug exception,可以在code里在某種error的情形下調用。BUG_ON()也是調用的這個macro,只是多一個判斷條件。

BUG() macro的實現

arch/arm64/include/asm/asm-bug.h

#ifdef CONFIG_DEBUG_BUGVERBOSE
#define _BUGVERBOSE_LOCATION(file, line) __BUGVERBOSE_LOCATION(file, line)
#define __BUGVERBOSE_LOCATION(file, line)            \
        .pushsection .rodata.str,"aMS",@progbits,1;    \
    14472:    .string file;                    \ #這里.string存儲__FILE__ string,會自動以/0字符結尾
        .popsection;                    \
                                \
        .long 14472b - 14470b;                \  #這里存儲的是上面.string字串在bug_entry struct里的offset,后面根據bug_entry struct的起始地址再加上這個offset即可以得到這個string的起始地址
        .short line;   #存儲的是__LINE__
#else
#define _BUGVERBOSE_LOCATION(file, line)
#endif

#ifdef CONFIG_GENERIC_BUG

#define __BUG_ENTRY(flags)                 \
        .pushsection __bug_table,"aw";        \
        .align 2;                \
    14470:    .long 14471f - 14470b;            \   #這里是兩個label地址相減,這個對應bug_entry struct里的bug_addr_disp,即這個struct的size
_BUGVERBOSE_LOCATION(__FILE__, __LINE__)        \
        .short flags;                 \
        .popsection;                \
    14471:
#else
#define __BUG_ENTRY(flags)
#endif

#define ASM_BUG_FLAGS(flags)                \
    __BUG_ENTRY(flags)                \
    brk    BUG_BRK_IMM

#define ASM_BUG()    ASM_BUG_FLAGS(0)

 

 

注意上面__BUG_ENTRY macro里看起來是有5個field,但是它是對應如下struct bug_entry的,這個struct里只define了4個field,所以看起來上面__BUG_ENTRY里的.string是不包含在struct bug_entry里的

include/asm-generic/bug.h

#ifdef CONFIG_GENERIC_BUG
struct bug_entry {
#ifndef CONFIG_GENERIC_BUG_RELATIVE_POINTERS
    unsigned long    bug_addr;
#else
    signed int    bug_addr_disp;
#endif
#ifdef CONFIG_DEBUG_BUGVERBOSE
#ifndef CONFIG_GENERIC_BUG_RELATIVE_POINTERS
    const char    *file;
#else
    signed int    file_disp;
#endif
    unsigned short    line;
#endif
    unsigned short    flags;
};
#endif    /* CONFIG_GENERIC_BUG */

 

 

arch/arm64/kernel/traps.c

static int bug_handler(struct pt_regs *regs, unsigned int esr)
{
    if (user_mode(regs))
        return DBG_HOOK_ERROR;

    switch (report_bug(regs->pc, regs)) {
    case BUG_TRAP_TYPE_BUG:
        die("Oops - BUG", regs, 0);
        break;

    case BUG_TRAP_TYPE_WARN:
        break;

    default:
        /* unknown/unrecognised bug trap type */
        return DBG_HOOK_ERROR;
    }

    /* If thread survives, skip over the BUG instruction and continue: */
    arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE);
    return DBG_HOOK_HANDLED;
}

 

4.19/lib/bug.c
enum
bug_trap_type report_bug(unsigned long bugaddr, struct pt_regs *regs) { struct bug_entry *bug; const char *file; unsigned line, warning, once, done; if (!is_valid_bugaddr(bugaddr)) return BUG_TRAP_TYPE_NONE; bug = find_bug(bugaddr); ... if (file) pr_crit("kernel BUG at %s:%u!\n", file, line); else pr_crit("Kernel BUG at %pB [verbose debug info unavailable]\n", (void *)bugaddr);

 

 

4.19/lib/bug.c

static inline unsigned long bug_addr(const struct bug_entry *bug)
{
#ifndef CONFIG_GENERIC_BUG_RELATIVE_POINTERS
    return bug->bug_addr;
#else
    return (unsigned long)bug + bug->bug_addr_disp;
#endif
}

 


ASM_BUG_FLAGS里包含了__BUG_ENTRY(flags)以及一條brk BUG_BRK_IMM指令,

__BUG_ENTRY相當於define了一個struct bug_entry,所以是在這個struct后面緊跟了一個brk BUG_BRK_IMM指令,所以上述(unsigned long)bug + bug->bug_addr_disp即表示這條brk指令的地址,這個地址和發生brk異常時的PC值比較,如果相等,則找到了對應的bug_entry struct,並將調用BUG_ON()所在文件以及行數打印出來

 

看一下BUG_ON() 是如何實現的

include/asm-generic/bug.h
#ifndef HAVE_ARCH_BUG_ON
#define BUG_ON(condition) do { if (unlikely(condition)) BUG(); } while (0)
#endif

 

4.19/arch/arm64/include/asm/bug.h
#define BUG() do {                    \
    __BUG_FLAGS(0);                    \
    unreachable();                    \
} while (0)

 

#define __BUG_FLAGS(flags)                \
    asm volatile (__stringify(ASM_BUG_FLAGS(flags)));

 

所以每一個BUG_ON最終的編譯結果會包含一個brk指令,如果BUG_ON的條件成立(條件判斷為1),則會執行到這條brk指令觸發異常,處理這個異常的callstack如下:

<2>[ 1409.592923] kernel BUG at block/blk-core.c:3238!

<0>[ 1409.592929] Internal error: Oops - BUG: 0 [#1] PREEMPT SMP

KERNEL-PANIC

dump process path

[HwBinder:2904_2, 3479]

dump backtrace

[<0000000080c43ddd>] kmsg_dump+0xa4/0x284

[<000000000affdfa0>] panic+0x1c0/0x4e4

[<000000009de7d86d>] die+0x5d8/0x5ec

[<000000000b44dbbb>] bug_handler+0x50/0xa4

[<0000000014428671>] brk_handler+0xe0/0x21c

[<0000000056de13f5>] do_debug_exception+0x154/0x2a4

[<000000006312a774>] el1_dbg+0x18/0xa8

[<00000000723f061b>] blk_finish_request+0x22c/0x230

[<0000000036ccd665>] scsi_end_request+0x288/0x588

[<000000002223f37c>] scsi_io_completion+0x8c/0x90c

[<00000000404d6d57>] scsi_finish_command+0x12c/0x184

[<00000000323c7119>] scsi_softirq_done+0x118/0x14c

[<000000007f25ecd3>] blk_done_softirq+0xcc/0x130

[<00000000414d4713>] __do_softirq+0x1f8/0x490

[<000000003feb2504>] irq_exit+0x1d4/0x244

[<00000000df9c9f02>] handle_IPI+0x2cc/0x6b8

[<000000005f9611a3>] gic_handle_irq+0xa4/0xbc

[<00000000fe8e935c>] el1_irq+0xe8/0x190

[<00000000f48fe71e>] binder_ioctl_write_read+0x2868/0x3214

[<000000000c8156f9>] binder_ioctl+0x370/0xd30

[<0000000047fd89c2>] do_vfs_ioctl+0x718/0xf0c

[<0000000072e174c9>] __arm64_sys_ioctl+0xcc/0x104

[<00000000b02b22b0>] el0_svc_common+0xb8/0x1b8

[<0000000016fc6b90>] el0_svc_handler+0x74/0x90

[<00000000255fbc3e>] el0_svc+0x8/0x340

[<0000000074c12ba5>] 0xffffffffffffffff

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM