Search code examples
gccmakefilelinux-kernelgdbdwarf

How to compile the Linux kernel with -O0 for more detailed debug?


I'm hacking the Linux kernel v5.15 and try to debug it with gdb line by line. I've opened the dwarf debug info through make menuconfig. However, it seems that some line will still be skipped. I find that the default compile optimization is -O2. So I changed the Makefile like:

ifdef CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE
KBUILD_CFLAGS += -O2 # here change to -O0
else ifdef CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3
KBUILD_CFLAGS += -O3
else ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
KBUILD_CFLAGS += -Os
endif

Then I compile again:

make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- -j$(nproc)

But it gives:

  CALL    scripts/checksyscalls.sh
  CALL    scripts/atomic/check-atomics.sh
  CC      init/main.o
In file included from ././include/linux/compiler_types.h:85,
                 from <command-line>:
./arch/riscv/include/asm/jump_label.h: In function ‘want_init_on_alloc’:
./include/linux/compiler-gcc.h:88:38: warning: ‘asm’ operand 0 probably does not match constraints
   88 | #define asm_volatile_goto(x...) do { asm goto(x); asm (""); } while (0)
      |                                      ^~~
./arch/riscv/include/asm/jump_label.h:20:9: note: in expansion of macro ‘asm_volatile_goto’
   20 |         asm_volatile_goto(
      |         ^~~~~~~~~~~~~~~~~
./include/linux/compiler-gcc.h:88:38: error: impossible constraint in ‘asm’
   88 | #define asm_volatile_goto(x...) do { asm goto(x); asm (""); } while (0)
      |                                      ^~~
./arch/riscv/include/asm/jump_label.h:20:9: note: in expansion of macro ‘asm_volatile_goto’
   20 |         asm_volatile_goto(
      |         ^~~~~~~~~~~~~~~~~
./arch/riscv/include/asm/jump_label.h: In function ‘want_init_on_free’:
./include/linux/compiler-gcc.h:88:38: warning: ‘asm’ operand 0 probably does not match constraints
   88 | #define asm_volatile_goto(x...) do { asm goto(x); asm (""); } while (0)
      |                                      ^~~
./arch/riscv/include/asm/jump_label.h:20:9: note: in expansion of macro ‘asm_volatile_goto’
   20 |         asm_volatile_goto(
      |         ^~~~~~~~~~~~~~~~~
make[1]: *** [scripts/Makefile.build:277: init/main.o] Error 1
make: *** [Makefile:1868: init] Error 2

If the optimization parameters really cannot be changed, is there a way to debug the kernel with finer granularity?


Solution

  • However, it seems that some line will still be skipped. I find that the default compile optimization is -O2.

    This is normal, kernel code is not written to be compiled with optimizations turned off. In fact, it heavily relies on compiler optimizations, and from my own experience I know for a fact that it won't compile with -O0. It may compile with -O1 in some cases, but I don't think that's a guarantee.

    You can try with -O1, that will get you somewhere, but I would recommend keeping the default of -O2. And of course, enable the generation of debugging information in your configuration (in make menuconfig, under "Kernel hacking" -> "Compile-time checks and compiler options"). There is also CONFIG_READABLE_ASM that if enabled will try generating more human readable assembly code.

    is there a way to debug the kernel with finer granularity?

    Not really. The only real way to do this is to be proficient enough in assembly to understand what is going on, what got optimized and what did not. Even with lower optimization levels, a lot of kernel code uses static and inline functions that will get simplified and inlined by the compiler. Debugging that kind of code you will see GDB jumping all over the place constantly. You cannot do much about it.

    Using GDB with layout split is the way to go, to show both code and assembly together. Have a look at this GDB doc page and this other question for more insights.

    If you want to get proficient at debugging in this way, start by studying what is the calling convention on the architecture you are working on and what instructions are used for calls, returns and branches. That will make you understand what parts of the assembly match the C code at a glance. Other than that, it's only a matter of practice.