• 驱动模块(3)——Linux交叉编译工具链


    所有的工具:

    aarch64-poky-linux-addr2line
    aarch64-poky-linux-c++filt
    aarch64-poky-linux-g++
    aarch64-poky-linux-gcc-nm
    aarch64-poky-linux-gprof
    aarch64-poky-linux-nm
    aarch64-poky-linux-readelf
    aarch64-poky-linux-ar
    aarch64-poky-linux-cpp
    aarch64-poky-linux-gcc
    aarch64-poky-linux-gcc-ranlib
    aarch64-poky-linux-ld
    aarch64-poky-linux-objcopy
    aarch64-poky-linux-size
    aarch64-poky-linux-as
    aarch64-poky-linux-dwp
    aarch64-poky-linux-gcc-5.2.1
    aarch64-poky-linux-gcov
    aarch64-poky-linux-ld.bfd
    aarch64-poky-linux-objdump
    aarch64-poky-linux-strings
    aarch64-poky-linux-c++
    aarch64-poky-linux-elfedit
    aarch64-poky-linux-gcc-ar
    aarch64-poky-linux-gcov-tool
    aarch64-poky-linux-ld.gold
    aarch64-poky-linux-ranlib
    aarch64-poky-linux-strip


    但是编译成模块的符号并不在vmlinux中,因此下面这样使用没有效果

    查找某个内核符号的位置:
    $ aarch64-poky-linux-nm vmlinux | grep vmalloc_user
    ffff00000819f030 T vmalloc_user

    这个内核符号(地址)对应源文件的哪一行
    $ aarch64-poky-linux-addr2line -e vmlinux ffff00000819f030
    /media/ubuntu/work/Yocto34/source/linux/mm/vmalloc.c:1870

    对模块进行反汇编:

    aarch64-poky-linux-objdump -D -S ehci-hcd.ko > ehci-hcd.dump

    由于ehci_hcd.c编译成模块了,但是偏移还是可以正常使用的

    [ 5360.731638] PC is at qh_completions+0x410/0x4e0 [ehci_hcd]
    [ 5360.737134] LR is at end_unlink_async+0x18c/0x2b0 [ehci_hcd]
    [ 5360.927298] [<ffff000000b913c0>] qh_completions+0x410/0x4e0 [ehci_hcd]
    [ 5360.933839] [<ffff000000b92994>] end_unlink_async+0x18c/0x2b0 [ehci_hcd]
    [ 5360.940553] [<ffff000000b92afc>] end_iaa_cycle+0x44/0x50 [ehci_hcd]

    使用将模块进行反汇编后的代码:
    0000000000002fb0 <qh_completions>:
    * Process and free completed qtds for a qh, returning URBs to drivers.
    * Chases up to qh->hw_current. Returns nonzero if the caller should
    * unlink qh.
    */
    static unsigned qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
    {
    2fb0: a9b87bfd stp x29, x30, [sp, #-128]!
    2fb4: 910003fd mov x29, sp
    2fb8: a90153f3 stp x19, x20, [sp, #16]
    2fbc: aa0003f4 mov x20, x0
    2fc0: a9025bf5 stp x21, x22, [sp, #32]
    2fc4: 91006035 add x21, x1, #0x18
    2fc8: a90573fb stp x27, x28, [sp, #80]
    ....

    内核中每一个存在的符号都如0000000000002fb0 <qh_completions>:对于显示指定的inline函数和编译器优化的inline函数,其样子如下,是没有符号地址的。
    static inline void ehci_qtd_free (struct ehci_hcd *ehci, struct ehci_qtd *qtd)
    {
    dma_pool_free (ehci->qtd_pool, qtd, qtd->qtd_dma);
    31a4: f9414280 ldr x0, [x20, #640]
    31a8: aa1a03e1 mov x1, x26
    31ac: f9401f42 ldr x2, [x26, #56]
    31b0: 94000000 bl 0 <dma_pool_free>
    ...

    对于qh_completions+0x410,只需要使用0000000000002fb0+0x410即可找出出问题的地址

    Oops 信息包含以下几部分内容。
    1.一段文本描述信息。
    eg: “Unable to handle kernel NULL pointer dereference at virtual address 00000000” 的信息,它说明了发生的是哪类错误。
    2.Oops 信息的序号。
    eg: Internal error: Oops: 805 [#1] 比如是第 1 次、第 2 次等。这些信息与下面类似,中括号内的数据表示序号。
    3.内核中加载的模块名称,也可能没有,以下面字样开头。
    Modules linked in:
    4.发生错误的 CPU 的序号,对于单处理器的系统,序号为 0,比如:
    CPU: 0 Not tainted (2.6.22.6 #36)
    5.发生错误时 CPU 的各个寄存器值。
    6.当前进程名及进程PID
    eg:Process swapper (pid: 1, stack limit = 0xc0480258) 这并不是说发生错误的是这个进程,而是表示发生错误时,当前进程是它。
    错误可能发生在内核代码、驱动程序,也可能就是这个进程的错误。
    7.栈信息。
    8.栈回溯信息,可以从中看出函数调用关系,形式如下:
    Backtrace:
    [<c001a6f4>] (s3c2410fb_probe+0x0/0x560) from [<c01bf4e8>] (platform_drv_
    probe+0x20/0x24)
    ...
    9.出错指令附近的指令的机器码
    eg:Code: e24cb004 e24dd010 e59f34e0 e3a07000 (e5873000) (出错指令在小括号里)

    要让内核出错时能够打印栈回溯信息,编译内核时要增加“-fno-omit-frame-pointer”选
    项,这可以通过配置 CONFIG_FRAME_POINTER 来实现。查看内核目录下的配置文件.config,
    确保 CONFIG_FRAME_POINTER 已经被定义

    补充:

    1.要构建外部模块,您必须具有可用的预构建内核,其中包含构建中使用的配置和头文件。

    2.编译模块时生成的其它文件:
    modules.order: 编译出的模块的存放路径
    Module.symvers: 编译的模块通过EXPORT_SYMBOL/EXPORT_SYMBOL_GPL导出的符号,其存储语法:
    <CRC> <Symbol> <module>

    3.与体系结构相关的头文件存放在arch/$(ARCH)/include/下,无关的在Linux

    4.编译模块时额外指定头文件搜索路径的方法
    obj-m := complex.o
    ccflags-y := -I$(PWD)/include //指定额外头文件搜索路径

    5.Makefile指定编译子目录 obj-y := foo/ bar/

    6.modules.builtin(lib/modules下)文件列出了内核中内置的所有模块。modprobe使用它在尝试加载内置函数时不会失败。

    7.编译内核:
    # cp arch/arm/configs/xxxx .config
    # make

    8.导出内核头文件到用户空间
    make headers_install ARCH=arm INSTALL_HDR_PATH=/xxx
    /xxx默认是/user。
    ARCH指定导出哪个体系放入头文件。make headers_install_all会导出所有体系的头文件。
    要查看支持的体系结构的完整列表,请使用以下命令:ls -d include/asm-* | sed 's/.*-//

  • 相关阅读:
    CI框架主题切换的功能
    centos7 编译安装 php7.4
    单用户登陆demo-后者挤到前者,类似QQ
    nginx 负载均衡的配置
    PHP计算每月几周,每周的开始结束日期
    Centos7 编译安装PHP7
    TP 3.2.3 接入PHPMailer
    外部js引用vue实例环境的方式
    linux常用命令
    计算机中的二级制
  • 原文地址:https://www.cnblogs.com/hellokitty2/p/9745959.html
走看看 - 开发者的网上家园