1

Test is on x86 32bit Linux, Ubuntu 12.04, GCC 4.6.3 objdump 2.22

Basically when I use gcc to produce assembly code of function foo like this:

gcc -S foo.c -O2

At the end of function foo, I can get a sequence of instructions like this (I modified it and attached each instruction with its machine code to make it clear):

             ......
1977                                                 .cfi_restore_state
1978  8B150000 0000                                  movl    nodes, %edx
1979  89442410                                       movl    %eax, 16(%esp)
1980  A1000000 00                                    movl    i_depth, %eax
1981  8974240C                                       movl    %esi, 12(%esp)
1982  C7442404 FC000000                              movl    $.LC55, 4(%esp)
1983  89542414                                       movl    %edx, 20(%esp)
1984  89442408                                       movl    %eax, 8(%esp)
1985  C7042401 000000                                movl    $1, (%esp)
1986  E8FCFFFF FF                                    call    __printf_chk
1987  E937FFFF FF                                    jmp     .L181
1988                                         .L186:
1989  E8FCFFFF FF                                    call    __stack_chk_fail

foo1:

Which looks normal.

However, when I compiled + linked to create the ELF executable file, and then disassembly it with objdump like this:

gcc foo.c -O2
objdump -Dr -j .text foo

The instruction produced by disassembler looks like this (I modified a little bit to make it easier to understand):

11856 89442410                                mov %eax,0x10(%esp)
11857 A1000000 00                             mov 0x80851AC,%eax
11858 8974240C                                mov %esi,0xC(%esp)
11859 C7442404 00000000                       movl $S_0x8064658,0x4(%esp)
11860 89542414                                mov %edx,0x14(%esp)
11861 89442408                                mov %eax,0x8(%esp)
11862 C7042401 000000                         movl $0x1,(%esp)
11863 E8FCFFFF FF                             call __printf_chk
11864 E933FFFF FF                             jmp 0x80547EB
11865
11866 E8FCFFFF FF                             S_0x80548BC : call __stack_chk_fail
11867 EB0D                                    jmp foo1
11868 90                                      nop
11869 90                                      nop
11870 90                                      nop
11871 90                                      nop
11872 90                                      nop
11873 90                                      nop
11874 90                                      nop
11875 90                                      nop
11876 90                                      nop
11877 90                                      nop
11878 90                                      nop
11879 90                                      nop
11880 90                                      nop
11881                                         foo1:

Looking at the end of function foo, I find out a sequence of instructions which can not be found in the original assembly code.

It seems like a padding issue, but I am not sure.

So my questions are:

  1. What's these instruction sequences for?
  2. Is there anyway to tell (assembler? linker?) do not generate these instruction sequences..? Because basically I am working a assembly code analysis tool, and these instruction sequences annoying the coding much.
1

What's these instruction sequences for?

They are for code optimization.

CPU cache

To optimize memory accesses, the CPU uses its own (small) internal memory called cache. It usually consists of several levels named L1, L2 etc. A lower suffix number means that the memory is located closer to the CPU core, thus is faster to access, but it's smaller as well. An illustration of this concept taken from link is given below:

CPU cache levels

Accessing the CPU cache is critically faster than reading RAM memory (see this question for more information) and that's why it is better to have data in cache instead of reading it each time from RAM (or even worse - hard disk).

But the CPU doesn't cache only data - it caches instructions as well. And for instructions to be cached effectively, they have to be properly aligned. Following cite comes from here:

Most microprocessors fetch code in aligned 16-byte or 32-byte blocks. If an important subroutine entry or jump label happens to be near the end of a 16-byte block then the microprocessor will only get a few useful bytes of code when fetching that block of code. It may have to fetch the next 16 bytes too before it can decode the first instructions after the label.

This can be avoided by aligning important subroutine entries and loop entries by 16. [...] We may align subroutine entries by the cache line size (typically 64 bytes) if the subroutine is part of a critical hot spot and the preceding code is unlikely to be executed in the same context.

So, it may be the case that under foo1: there is some short loop and compiler decided to align this block to put it in the CPU cache so it is executed faster.

As @user45891 already stated in the comment, such an optimization in gcc is turned on with the option -O2, so don't use it when you don't want such optimizations.

But why the difference between the two outputs?

Because the first result comes from just two first states of compilation performed by gcc (link):

Compilation can involve up to four stages: preprocessing, compilation proper, assembly and linking, always in that order.

-S

Stop after the stage of compilation proper; do not assemble.

While the second one is "entirely compiled" and linked.

  • 1
    WOW, thank you very much for this answer. It has been about five years and I am still in the reverse engineering business. God bless us. – lllllllllllll Jun 30 at 11:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.