2

I have some kernel Oops which failed here:

BUG: ...
IP: [<ffffffffabcdefab>] myfunction+0x10/0x1e [mymodule]

In Oops we can see that the function length is 30 bytes in decimal.
I suppose that length is amount of bytes from 1st byte of 1st instruction till 1st byte of last instruction. I.e. scatter from 1st instruction's address till last instruction's address. Am I right?

So how could one ensure that myfunction is 30 bytes length viewing objdump output? Just subtracting address of 1st instruction from address of the last one?

F.e.:

0000000000068930 <myfunction>:
   68930: 53                       push   %rbx   
   68931: 48 8b 07                 mov    (%rdi),%rax
   68934: 48 89 fb                 mov    %rdi,%rbx
   68937: ff 10                    callq  *(%rax)
   68939: 80 7b 08 00              cmpb   $0x0,0x8(%rbx)
   6893d: 75 09                    jne    68948 <foo1+0x20>
   6893f: 5b                       pop    %rbx   
   68940: c3                       retq
   68941: 0f 1f 80 00 00 00 00     nopl   0x0(%rax)
   68948: 48 89 df                 mov    %rbx,%rdi
   6894b: 5b                       pop    %rbx   
   6894c: eb a2                    jmp    688f0 <foo2>
   6894e: 66 90                    xchg   %ax,%ax

Can we tell that myfunction's length is 0x6894e - 0x68930= 1e (30 bytes in decimal) from output of objdump? If no, what is the length of function in terms of disassembly?

z0lupka
  • 236
  • 4
  • 19

1 Answers1

2

It should really be the number of bytes from the beginning of the first instruction, to the end of the last instruction, inclusive. This is equivalent to taking the address of the byte after the last instruction, and subtracting the address of the first byte of the first instruction.

In this case, the last instruction of your function is actually the jmp 688f0 which begins at 6894c, and so the byte following this instruction is at 6894e. The xchg %ax, %ax instruction is not really part of your function; note that it isn't reachable from anywhere in your function. It's a no-op instruction that's been added by the compiler as padding, so that the next function can be aligned on an 8- or 16-byte boundary (which is better for caching, etc).

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82
  • *the last instruction...is actually* - You sure that Oops ignores padding instructions while calculation of common size of function? It's important in case when I should compare the value from Oops with the value counted by myself – z0lupka Dec 05 '19 at 16:44
  • 2
    @z0lupka: presumably it uses object file metadata. GCC uses the `.size` directive to embed a size for each symbol into `.o` files; unless this gets stripped out it's still there. But objdump doesn't use that so we're left looking by eye for how far forward/backward the function can actually jump from any of its conditional branches to find its basic blocks. – Peter Cordes Dec 05 '19 at 16:47
  • @PeterCordes Ok, so can we assume that the `.size` directive **ignores** such **padding** instructions **or not** without deep diving into GCC sources? – z0lupka Dec 05 '19 at 16:53
  • 1
    @z0lupka: you can just look at GCC's asm output (https://godbolt.org/z/pCYu3s). GCC doesn't have to "ignore" padding in calculating the size, it knows what instructions it's emitting to implement the function so it only counts those. The padding gets added later. (Technically GCC emits a `.size . - symbol` directive before a `.p2align` directive that creates the padding, or before the end of a file where a linker might add padding) – Peter Cordes Dec 05 '19 at 17:07
  • 1
    @PeterCordes perhaps I confused you about my function's size in trace `0x1e`. It was a random number I came up with. Literally in all the Oops listings I've seen, the length of the function is a multiple of 16. I don't think it's a coincidence. So I can conclude, that the size of function includes "padding-instructions". Or is it still a coincidence? – z0lupka Dec 06 '19 at 14:07
  • 1
    Function *start* addresses are always going to be aligned by 16, but lengths don't have to be. If they are, then probably the kernel is looking for the next label address and subtracting, which *would* include the padding. In which case yes your faked up output showing a 0x1e length that isn't a multiple of 16 is highly misleading. I concluded that it must be using `.size` metadata because otherwise it couldn't know where the padding was. (Other than disassembly and recognition of unconditional branch instructions, and knowing the difference between tailcalls to other functions...) – Peter Cordes Dec 07 '19 at 03:57