gcc O0的正确使用姿势

简介: 今天在调试内核的时候, 发现一个变量指针在一个函数中变了, 但是代码中又没有改变他的值 ``` (gdb) p sc $13 = (struct scan_control *) 0xffffc900079b3da8 (gdb) n (gdb) p sc $14 = (struct scan_control *) 0xffff880a4c55a800 ``` 根据以往的经验, 肯定是

今天在调试内核的时候, 发现一个变量指针在一个函数中变了, 但是代码中又没有改变他的值

(gdb) p sc
$13 = (struct scan_control *) 0xffffc900079b3da8
(gdb) n
(gdb) p sc
$14 = (struct scan_control *) 0xffff880a4c55a800

根据以往的经验, 肯定是gcc的debuginfo又不对了, 因为以前干过这种事情

(从一个地方看来的, 没有求证过, gcc每次编译完会自检
一共编译3边, 同一份源代码, 系统gcc产生gcc1, gcc1产生gcc2, gcc2产生gcc3
gcc1和gcc2的二进制可能会不一样, 虽然源代码一样, 但是编译器不一样
gcc2和gcc3的二进制肯定会一样, 源代码一样, 编译器的二进制不一样, 但是编译器的行为是一样的
所以自检只会检查代码正确性, 不会检查到debuginfo)

源代码

用了O0函数属性
__attribute__((optimize("-O0"))) static bool kswapd_shrink_node(pg_data_t *pgdat,
                   struct scan_control *sc)
{
    struct zone *zone;
    int z;
    sc->nr_to_reclaim = 0;
............
    return sc->nr_scanned >= sc->nr_to_reclaim;
}

检查汇编

00000000000074d0 <kswapd_shrink_node>:
    74d0:       e8 00 00 00 00          callq  74d5 <kswapd_shrink_node+0x5>
    74d5:       55                      push   %rbp
    74d6:       48 89 e5                mov    %rsp,%rbp
    74d9:       53                      push   %rbx
    74da:       48 83 ec 38             sub    $0x38,%rsp
    74de:       48 89 7d c8             mov    %rdi,-0x38(%rbp) pgdat没错, 是O0, 把参数保存成局部变量
    74e2:       48 89 75 c0             mov    %rsi,-0x40(%rbp) sc
    74e6:       65 48 8b 04 25 28 00    mov    %gs:0x28,%rax

检查debuginfo

#objdump --dwarf=info mm/vmscan.o |grep kswapd_shrink_node -A 25
    <26545>   DW_AT_name        : (indirect string, offset: 0xe966): kswapd_shrink_node
    <26549>   DW_AT_decl_file   : 1
    <2654a>   DW_AT_decl_line   : 3190
    <2654c>   DW_AT_prototyped  : 1
    <2654c>   DW_AT_type        : <0x25e>
    <26550>   DW_AT_low_pc      : 0x74d0
    <26558>   DW_AT_high_pc     : 0x144
    <26560>   DW_AT_frame_base  : 1 byte block: 9c      (DW_OP_call_frame_cfa)
    <26562>   DW_AT_GNU_all_call_sites: 1
    <26562>   DW_AT_sibling     : <0x26636>
 <2><26566>: Abbrev Number: 110 (DW_TAG_formal_parameter)
    <26567>   DW_AT_name        : (indirect string, offset: 0xa7ed): pgdat
    <2656b>   DW_AT_decl_file   : 1
    <2656c>   DW_AT_decl_line   : 3190
    <2656e>   DW_AT_type        : <0xf0d8>
    <26572>   DW_AT_location    : 1 byte block: 55      (DW_OP_reg5 (rdi)) debuginfo居然还是rdi
 <2><26574>: Abbrev Number: 140 (DW_TAG_formal_parameter)
    <26576>   DW_AT_name        : sc
    <26579>   DW_AT_decl_file   : 1
    <2657a>   DW_AT_decl_line   : 3191
    <2657c>   DW_AT_type        : <0x13f59>
    <26580>   DW_AT_location    : 1 byte block: 54      (DW_OP_reg4 (rsi)) 同样的问题
 <2><26582>: Abbrev Number: 119 (DW_TAG_variable)

看起来是gcc的问题, 因为cmdline里面是O2, 但是文件里面是O0, 所以代码按照O0来生成, 但是debuginfo用O2的来生成

网上暂时没找到方法解决, 估计这样用的人比较少

看起来cmdline要用O0来能让gcc避免这个bug, 但是O0又编译不了vmscan.c, 所以最后只能这样用
cmdline先执行O0, 然后把O2的那些选项手动得加上, 这样才避免这个gcc bug

    <1f268>   DW_AT_name        : (indirect string, offset: 0xe812): kswapd_shrink_node
    <1f26c>   DW_AT_decl_file   : 56
    <1f26d>   DW_AT_decl_line   : 3190
    <1f26f>   DW_AT_prototyped  : 1
    <1f26f>   DW_AT_type        : <0x25e>
    <1f273>   DW_AT_low_pc      : 0xcf63
    <1f27b>   DW_AT_high_pc     : 0x136
    <1f283>   DW_AT_frame_base  : 1 byte block: 9c      (DW_OP_call_frame_cfa)
    <1f285>   DW_AT_GNU_all_tail_call_sites: 1
    <1f285>   DW_AT_sibling     : <0x1f2f5>
 <2><1f289>: Abbrev Number: 91 (DW_TAG_formal_parameter)
    <1f28a>   DW_AT_name        : (indirect string, offset: 0xa673): pgdat
    <1f28e>   DW_AT_decl_file   : 56
    <1f28f>   DW_AT_decl_line   : 3190
    <1f291>   DW_AT_type        : <0xf434>
    <1f295>   DW_AT_location    : 3 byte block: 91 b8 7f        (DW_OP_fbreg: -72)这样就没有问题了
 <2><1f299>: Abbrev Number: 96 (DW_TAG_formal_parameter)
    <1f29a>   DW_AT_name        : sc
    <1f29d>   DW_AT_decl_file   : 56
    <1f29e>   DW_AT_decl_line   : 3191
    <1f2a0>   DW_AT_type        : <0x1aae2>
    <1f2a4>   DW_AT_location    : 3 byte block: 91 b0 7f        (DW_OP_fbreg: -80)
 <2><1f2a8>: Abbrev Number: 92 (DW_TAG_variable)
    <1f2a9>   DW_AT_name        : (indirect string, offset: 0xb7ce): zone
    <1f2ad>   DW_AT_decl_file   : 56
    <1f2ae>   DW_AT_decl_line   : 3193
目录
相关文章
|
7月前
|
编译器 Linux 开发工具
|
7月前
|
NoSQL 编译器 开发工具
006.gcc编译器
gcc是什么?
87 0
006.gcc编译器
|
7月前
|
存储 NoSQL 算法
从一个crash问题展开,探索gcc编译优化细节
问题分析的过程也正是技术成长之路,本文以一个gcc编译优化引发的crash为切入点,逐步展开对编译器优化细节的探索之路,在分析过程中打开了新世界的大门……
|
4月前
|
前端开发 C语言
gcc动态库升级
gcc动态库升级
|
2月前
|
编译器 Linux C语言
gcc的编译过程
GCC(GNU Compiler Collection)的编译过程主要包括四个阶段:预处理、编译、汇编和链接。预处理展开宏定义,编译将代码转换为汇编语言,汇编生成目标文件,链接将目标文件与库文件合并成可执行文件。
70 11
|
4月前
|
编译器 开发工具 C语言
Gcc 链接文件
Gcc 链接文件
38 4
|
4月前
|
编译器 C语言 C++
MinGW安装gcc
MinGW安装gcc
93 0
|
6月前
|
自然语言处理 编译器 Go
GCC:GNU编译器
GCC:GNU编译器
|
6月前
|
Java 编译器 Linux
技术经验解读:【转载】详解GCC的下载和安装(源码安装)
技术经验解读:【转载】详解GCC的下载和安装(源码安装)
176 0
|
7月前
|
C语言
gcc的简易用法
【5月更文挑战第10天】gcc的简易用法。
69 8