How to Generate Mixed Source and Assembly List from Source Code using GCC

Posted on In Linux, Programming

When debugging and optimizing programs, developers sometimes need to generate and investigate into the assembly generated by the compiler. Generating a mixed source and assembly list will help a lot for debugging and optimization. gcc can achieve this by working with the assembler.

Generate assembly list mixed with the source code

Just add these gcc compile options:

-Wa,-adhln -g

The command:

$ gcc -Wa,-adhln -g source_code.c > assembly_list.s

The options:

-g: Produce debugging information
-Wa,option: Pass option as an option to the assembler
-adhln:
a: turn on listings
d: omit debugging directives
n: omit forms processing
h: include high-level source
l: include assembly

One example

The source code:

helloworld.c:

#include <stdio.h>
int main()
{
  printf("Hello world!\n");
  return 0;
}

The command:

$ gcc -Wa,-adhln -g helloworld.c > helloworld.s

helloworld.s:

    ...

    0:helloworld.c  **** #include <stdio.h>
    1:helloworld.c  ****
    2:helloworld.c  **** int main()
    3:helloworld.c  **** {
    19                            .loc 1 4 0
    20                            .cfi_startproc
    21 0000 55                    pushq   %rbp
    22                    .LCFI0:
    23                            .cfi_def_cfa_offset 16
    24 0001 4889E5                movq    %rsp, %rbp
    25                            .cfi_offset 6, -16
    26                    .LCFI1:
    27                            .cfi_def_cfa_register 6
    4:helloworld.c  ****   printf("Hello world!\n");
    28                            .loc 1 5 0
    29 0004 BF000000              movl    $.LC0, %edi
    29      00
    30 0009 E8000000              call    puts
    30      00
    5:helloworld.c  ****   return 0;
    31                            .loc 1 6 0
    32 000e B8000000              movl    $0, %eax
    32      00
    6:helloworld.c  **** }

    ...

Another example with more than 1 source files

You may also use the method here for compiling programs from more than 1 C source files. For example, a project contains 2 source files as follows.

fun1.c:

#include <stdio.h>

void fun1()
{
    printf("Hello world!\n");
}

main.c:

#include <stdio.h>
void fun1();
int main()
{
    fun1();
    return 0;
}

You can generate the source-assembly list by

$ gcc -Wa,-adhln -g fun1.c main.c -o a > helloworld.s

For your reference, the helloworld.s file is as follows.

   1                    .file   "fun1.c"
   2                    .text
   3                .Ltext0:
   4                    .section    .rodata
   5                .LC0:
   6 0000 48656C6C      .string "Hello world!"
   6      6F20776F 
   6      726C6421 
   6      00
   7                    .text
   8                    .globl  fun1
  10                fun1:
  11                .LFB0:
  12                    .file 1 "fun1.c"
   1:fun1.c        **** #include <stdio.h>
   2:fun1.c        **** 
   3:fun1.c        **** void fun1()
   4:fun1.c        **** {
  13                    .loc 1 4 0
  14                    .cfi_startproc
  15 0000 55            pushq   %rbp
  16                    .cfi_def_cfa_offset 16
  17                    .cfi_offset 6, -16
  18 0001 4889E5        movq    %rsp, %rbp
  19                    .cfi_def_cfa_register 6
   5:fun1.c        ****     printf("Hello world!\n");
  20                    .loc 1 5 0
  21 0004 BF000000      movl    $.LC0, %edi
  21      00
  22 0009 E8000000      call    puts
  22      00
   6:fun1.c        **** }
  23                    .loc 1 6 0
  24 000e 5D            popq    %rbp
  25                    .cfi_def_cfa 7, 8
  26 000f C3            ret
  27                    .cfi_endproc
  28                .LFE0:
  30                .Letext0:
   1                    .file   "main.c"
   2                    .text
   3                .Ltext0:
   4                    .globl  main
   6                main:
   7                .LFB0:
   8                    .file 1 "main.c"
   1:main.c        **** #include <stdio.h>
   2:main.c        **** void fun1();
   3:main.c        **** int main()
   4:main.c        **** {
   9                    .loc 1 4 0
  10                    .cfi_startproc
  11 0000 55            pushq   %rbp
  12                    .cfi_def_cfa_offset 16
  13                    .cfi_offset 6, -16
  14 0001 4889E5        movq    %rsp, %rbp
  15                    .cfi_def_cfa_register 6
   5:main.c        ****     fun1();
  16                    .loc 1 5 0
  17 0004 B8000000      movl    $0, %eax
  17      00
  18 0009 E8000000      call    fun1
  18      00
   6:main.c        ****     return 0;
  19                    .loc 1 6 0
  20 000e B8000000      movl    $0, %eax
  20      00
   7:main.c        **** }
  21                    .loc 1 7 0
  22 0013 5D            popq    %rbp
  23                    .cfi_def_cfa 7, 8
  24 0014 C3            ret
  25                    .cfi_endproc
  26                .LFE0:
  28                .Letext0:

Eric Ma

Eric is a systems guy. Eric is interested in building high-performance and scalable distributed systems and related technologies. The views or opinions expressed here are solely Eric's own and do not necessarily represent those of any third parties.

17 comments

  1. Hi,
    I have found the above commands you have suggested quite useful but i would like to know how can i use this command when there are mutiple files of code to be executed and I am interested in the assembly code of only one of those files ?

    1. You can use the same method to generate the source-assembly list from several source files. Note that the line numbers are followed with source file names like “1:fun1.c”. You may only check the parts that you are interested in.

  2. Hi Sir,

    Thank you very much for your explanation.
    I have one query, So from the above option we can get the list of source file but is it possible that “Can we get to know what and particular source file is going to be used for particular executable file (In case when source code generate two or more executable’s)” ..?
    Lets say:
    We have 5 source file, and 3 source files are used for “a.out” and rest 2 source files are used for “b.out” then “How can we get to know it that which source files is used for which “executable file”..?

    Thanks in advance

  3. great post,

    Is their any tool to identify what are all different register used in assembly code generated by gcc or any other compiler? or is their any way we can find out which registers are used in assembly code and total access count of these registers.

    1. Different compilers may generate different instruction flows for the same program. It may not be easy to do a side-by-side comparison. There are only a small number of registers in the common CPU ISAs. To get best performance, compilers usually generate instructions to use as registers as much as possible.

      The total access count of the registers used may be counted during runtime and may be different with different input to the programs. If you need such info, you may use some techniques like those used in JIT compilers or virtual machines to “record” the instructions executed and count the register usage. But I am not aware of any existing tools doing this.

  4. This is very helpful. Could you please post also information what are corresponding compiler options for g++?

  5. getting errors at the end

    d:/progs/gcc_riscv_v12/bin/../lib/gcc/riscv-none-elf/12.2.0/../../../../riscv-none-elf/bin/ld.exe: warning: cannot find entry symbol _start; defaulting to 0000000000010094
    d:/progs/gcc_riscv_v12/bin/../lib/gcc/riscv-none-elf/12.2.0/../../../../riscv-none-elf/bin/ld.exe: C:\Users\mrx23dot\AppData\Local\Temp\cc8KCAnd.o: in function `adc_filter_task’:
    D:\DRIVE\Projects\FW_ch32v003_template\build/..\src\adcFilter/adcFilter.c:45: undefined reference to `adc_read’
    d:/progs/gcc_riscv_v12/bin/../lib/gcc/riscv-none-elf/12.2.0/../../../../riscv-none-elf/bin/ld.exe: C:\Users\mrx23dot\AppData\Local\Temp\cc8KCAnd.o: in function `adc_readFilt’:
    D:\DRIVE\Projects\FW_ch32v003_template\build/..\src\adcFilter/adcFilter.c:61: undefined reference to `assert_halt’
    collect2.exe: error: ld returned 1 exit status

    Tries to do more than asm?

Leave a Reply

Your email address will not be published. Required fields are marked *