Is there a way to isolate the assembly of a single function?

I am studying how a C file is compiled into machine code. I know that I can generate an assembly from gcc with the -S flag, however it also creates a lot of code for main() and printf() , which I am not interested in right now.

Is there a way to get gcc or clang to "compile" a function in isolation and output from an assembly?

those. get assembly for next c separately:

 int add( int a, int b ) { return a + b; } 
+6
source share
2 answers

There are two ways to do this for a specific object file:

  • The -ffunction-sections for gcc instructs it to create a separate ELF section for each function in the compiled source file.
  • The symbol table contains the name of the section, the starting address and size of this function; which can be passed to objdump with the arguments --start-address / --stop-address .

First example:

  $ readelf -S to |  grep '.text.'
   [1] .text PROGBITS 0000000000000000 00000040
   [4] .text.foo PROGBITS 0000000000000000 00000040
   [6] .text.bar PROGBITS 0000000000000000 00000060
   [9] .text.foo2 PROGBITS 0000000000000000 000000c0
   [11] .text.munch PROGBITS 0000000000000000 00000110
   [14] .text.startup.mai PROGBITS 0000000000000000 00000180 

This is compiled with -ffunction-sections , and there are four functions in my object file: foo() , bar() , foo2() and munch() . I can parse them separately:

  $ objdump -w -d --section = .text.foo to

 to: file format elf64-x86-64

 Disassembly of section .text.foo:

 0000000000000000 <foo>:
    0: 48 83 ec 08 sub $ 0x8,% rsp
    4: 8b 3d 00 00 00 00 mov 0 (% rip),% edi # a <foo + 0xa>
    a: 31 f6 xor% esi,% esi
    c: 31 c0 xor% eax,% eax
    e: e8 00 00 00 00 callq 13 <foo + 0x13>
   13: 85 c0 test% eax,% eax
   15: 75 01 jne 18 <foo + 0x18>
   17: 90 nop
   18: 48 83 c4 08 add $ 0x8,% rsp
   1c: c3 retq 

Another option can be used like this ( nm dumps character table entries):

  $ nm -f sysv to |  grep bar
 bar | 0000000000000020 |  T |  FUNC | 0000000000000026 |  | .text
 $ objdump -w -d --start-address = 0x20 --stop-address = 0x46 to --section = .text

 to: file format elf64-x86-64

 Disassembly of section .text:

 0000000000000020 <bar>:
   20: 48 83 ec 08 sub $ 0x8,% rsp
   24: 8b 3d 00 00 00 00 mov 0 (% rip),% edi # 2a <bar + 0xa>
   2a: 31 f6 xor% esi,% esi
   2c: 31 c0 xor% eax,% eax
   2e: e8 00 00 00 00 callq 33 <bar + 0x13>
   33: 85 c0 test% eax,% eax
   35: 75 01 jne 38 <bar + 0x18>
   37: 90 nop
   38: bf 3f 00 00 00 mov $ 0x3f,% edi
   3d: 48 83 c4 08 add $ 0x8,% rsp
   41: e9 00 00 00 00 jmpq 46 <bar + 0x26> 

In this case, the -ffunction-sections parameter was not used, so the initial offset of the function is not zero, and not in a separate section (but in .text ).

Beware when disassembling object files ...

This is not what you want, because the call targets for the object files (as well as the addresses of global variables) are not eliminated - you cannot see here that foo calls printf , because the resolution of this at the binary level occurs only during communication. However, the build source would have call printf . Information that this callq actually matches printf is in the object file, but separate from the code (it is in the so-called navigation section, which lists the locations in the object file that are β€œpatched” by the linker); the disassembler cannot resolve this.

+7
source

The best way would be to copy your function into a single temp.c C file and compile it using the -c flag as follows: gcc -c -S temp.c -o temp.s

It should produce more compressed assembly code without any other distraction (except for the header and footer).

0
source

Source: https://habr.com/ru/post/943974/