• 内核文档stackvalidation.txt中文


    翻译内核文档stack-validation.txt,难免有错误

    基于4.19.190内核,高版本内核中或许有更新

    内核 CONFIG_STACK_VALIDATION 选项启用objtool 在编译时运行。 它有一个“检查”子命令,分析每个 .o 文件并确保其堆栈元数据的有效性。
    它对 asm 代码和 C 内联汇编代码强制执行一组规则,因此堆栈跟踪是可靠的。对于每个函数,它递归地追踪所有可能的代码路径和在每条指令中验证
    正确的帧指针状态。

    它还跟踪涉及特殊部分的代码路径,例如.altinstructions、__jump_table 和 __ex_table,可以添加给定指令(或一组指令)的替代执行路径。
    同样,它知道如何遵循 switch 语句,因为其中 gcc 有时会使用跳转表。

    (Objtool 也有一个 'orc generate' 子命令,生成调试信息用于 ORC 开卷机。 请参阅文档/x86/orc-unwinder.txt内核树以获取更多详细信息。)

    Why do we need stack metadata validation?
    -----------------------------------------

    以下是验证堆栈元数据的一些好处:

    a) 为启用帧指针的内核提供更可靠的堆栈跟踪

    b) ORC(Oops Rewind Capability)展开表的生成

    c) 更高的实时补丁兼容率

    Rules
    -----

    为了实现验证,objtool 强制执行以下规则:

    1. 每个可调用函数都必须使用 ELF 函数类型进行注解。 在 asm 代码中,这通常使用
    ENTRY/ENDPROC 宏。 如果 objtool 在函数之外找到返回指令,它会标记一个错误,因为这通常表明应相应地注释可调用代码。
    这个规则是必要的,以便objtool能够正确地识别每个可调用函数,以便分析其堆栈元数据。

    2. 相反,每一段不能被调用的代码都不应该被注释为ELF函数。ENDPROC宏不应该在这种情况下使用。

    3.每个调用另一个函数的可调用函数必须具有正确的帧指针逻辑,如果需要CONFIG_FRAME_POINTER或体系结构的回链规则。这可以通过FRAME_BEGIN/FRAME_END宏在asm代码中完成。

    此规则确保基于帧指针的堆栈跟踪将按设计工作。 如果函数 A 在调用函数 B 之前没有创建堆栈帧,则函数 A 的_caller_ 将在堆栈追踪时被跳过。

    4. 只有在以下情况下才允许动态跳转和跳转到未定义的符号:

    a) 跳转是 switch 语句的一部分;
    b) 跳转匹配同级(兄弟)调用语义,并且帧指针具有与函数入口相同的值。

    需要此规则,以便 objtool 可以可靠地分析所有函数的代码路径。 如果一个函数跳转到另一个文件中的代码,
    而且不是同级(兄弟)调用,objtool没办法跟着跳转,因为它一次只分析一个文件。

    5. 可调用函数可能不执行内核进入/退出指令。唯一需要此类指令的代码是内核入口代码,无论如何,它不应该在可调用函数中。


    Objtool warnings
    ----------------
    对于 asm 文件,如果您遇到没有意义的错误,首先确保受影响的代码遵循上述规则。

    对于 C 文件,常见的罪魁祸首是内联 asm 语句和调用“noreturn”功能。 请参阅下面的更多细节。

    C 代码出错的另一个可能原因是如果 Makefile 删除了-fno-omit-frame-pointer 或将 -fomit-frame-pointer 添加到 gcc 选项。

    以下是 objtool 报告的一些常见警告示例、它们的含义以及如何修复它们的建议。

    1. file.o: warning: objtool: func()+0x128: call without frame pointer save/setup

    在启用 CONFIG_FRAME_POINTER的情况下,func() 函数在执行一个函数调用时没有先保存和/或更新帧指针。

    如果错误是针对 asm 文件,并且func()确实是可调用的函数,使用 FRAME_BEGIN和FRAME_END 宏添加适当的帧指针逻辑,
    如果它不是可调用函数,除它的 ELF 函数注释通过将 ENDPROC 更改为 END来删,而不是使用 asm/unwind_hints.h 中的手动展开提示宏。

    如果是 GCC 编译的 .c 文件,错误可能是因为函数使用具有“调用”指令的内联 asm() 语句。一个带有 call 指令的 asm() 语句必须其输出操作数中声明使用
    的堆栈指针。 在 x86_64 上,这意味着添加ASM_CALL_CONSTRAINT 作为输出约束:

    asm volatile("call func" : ASM_CALL_CONSTRAINT);

    否则堆栈帧可能不会在调用之前创建。


    2. file.o: warning: objtool: .text+0x53: unreachable instruction

    Objtool 找不到到达指令的代码路径。

    如果错误是针对 asm 文件,并且指令在一个(或可从)可调用函数内部(到达),该函数应该被注释,使用 ENTRY/ENDPROC 宏(ENDPROC 是重要的)。
    除此以外,代码可能应该使用asm/unwind_hints.h 中的展开提示宏进行注释,为了 objtool 和展开器可以知道与代码相关的堆栈状态。

    如果您 100% 确定代码不会影响堆栈跟踪,或者如果您仅仅只是一个bad person,你可以告诉 objtool 忽略它。 见“添加例外”一节。

    如果它实际上不在可调用函数中(例如内核入口代码),将 ENDPROC 更改为 END。

    4.file.o:警告:objtool:func():找不到开始指令
    或者
    file.o:警告:objtool:func()+0x11dd:无法解码指令

    该文件是否在text部分中有数据? 如果是这样,那可能会混淆objtool的指令解码器。 将数据移动到更合适的位置,.data 或 .rodata 之类的部分。

    5.file.o:警告:objtool:func()+0x6:可调用函数中不支持的指令
    这是一个内核进入/退出指令,如 sysenter 或 iret。这样的指令在可调用函数中不允许使用。并且很可能是内核入口代码的一部分。
    它们通常不应具有可调用函数注释 (ENDPROC),并且应始终使用 asm/unwind_hints.h 中的展开提示宏进行注释。

    6.file.o: 警告: objtool: func()+0x26: 来自可调用指令的同级调用,修改了堆栈帧
    这是动态跳转或跳转到未定义符号。 Objtool 假定它是同级调用并检测到帧指针没有首先恢复到其原始状态。
    如果它不是真正的同级调用,您可能需要将目标代码移动到本地文件。

    如果指令实际上不在可调用函数中(例如内核入口代码),将 ENDPROC 更改为 END 并手动注释
    asm/unwind_hints.h 中的展开提示宏。

    7. file: warning: objtool:func()+0x5c:堆栈状态不匹配
    指令的帧指针状态不一致,采用哪条执行路径到达指令。确保在启用 CONFIG_FRAME_POINTER 时,函数
    设置并入栈帧指针(for x86_64, this means rbp)在函数的开头,并在函数的末尾弹出它。

    还要确保函数中没有其他代码接触帧指针。
    另一种可能性是代码有一些汇编或内联汇编,它们对堆栈或帧指针做了一些不寻常的事情。在这种情况下,
    可能适合使用 asm/unwind_hints.h 中的展开提示宏。

    8. file.o: warning: objtool:objtool: funcA() “掉到”下一个函数 funcB()

    这意味着 funcA() 不会以返回指令或无条件跳转结束,并且objtool已经确定该函数可以落入下一个函数。
    这可能有不同的原因:

    1) funcA() 的最后一条指令是调用“noreturn”函数,例如 panic(),在这种情况下,需要将 noreturn 函数添加到
    objtool 的hard-coded global_noreturns 数组。指出错误给objtool 维护者,或者您可以提交补丁。
    2)funcA() 在一段实际可达的代码中使用了 unreachable() 注释。
    3) 如果 funcA() 调用内联函数,则 funcA() 的目标代码可能由于 gcc bug而损坏。 有关更多详细信息,请参阅:
    https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70646

    Adding exceptions
    -----------------

    如果你_真的_需要 objtool 来忽略某些东西,并且 100% 确定它不会影响内核堆栈跟踪,您可以告诉 objtool
    忽略它:


    - 要跳过函数验证,请使用 STACK_FRAME_NON_STANDARD
    宏。

    - 要跳过文件验证,请添加

    OBJECT_FILES_NON_STANDARD_filename.o := n 到 Makefile。

    - 要跳过目录验证,请添加

    OBJECT_FILES_NON_STANDARD := y 到 Makefile。

  • 相关阅读:
    Linux nfs服务讲解
    Linux nfs服务介绍
    牛客网题目-数组中只出现1次的数字
    牛客网中矩阵中的路径
    求链表的第一个公共节点
    C++中STL中简单的Vector的实现
    牛客网栈的压入,和弹出序列
    C++智能指针
    CI Weekly #22 | flow.ci 新版 iOS 构建流程的 4 大变化
    CI Weekly #21 | iOS 持续集成快速入门指南
  • 原文地址:https://www.cnblogs.com/lh03061238/p/16178112.html
Copyright © 2020-2023  润新知