0
obj.CurrSize -= size;
0x00000000003ad2d7 <+183>:  mov eax,0x0    
0x00000000003ad2dc <+188>:  test rax,rax
0x00000000003ad2df <+191>:  je 0x3ad2e6 <+198> 
0x00000000003ad2e1 <+193>:  call 0x0
0x00000000003ad2e6 <+198>:  mov rax, 0xffffffffffffff60 
0x00000000003ad2ed <+205>:  sub rbx,r15
0x00000000003ad2f0 <+208>:  add QWORD PTR fs:[rax],rbx`

Why test for zero, then if zero, jump over one instruction, but else do the same?

old_timer
  • 69,149
  • 8
  • 89
  • 168
vladon
  • 8,158
  • 2
  • 47
  • 91
  • Try removing that bit and see if it makes a difference. – Mad Physicist Sep 21 '17 at 17:53
  • @MadPhysicist ? – vladon Sep 21 '17 at 18:01
  • @PeterCordes it is clang with -O2. Look, there is eax zeroed, but rax tested. – vladon Sep 21 '17 at 18:16
  • 1
    If there are any nonzero bits in the upper half of `rax`, the `call 0x0` will be executed. So what comes after this? A `ret` with no stack cleanup? Which would then execute some of this code again, in a form of recursion. – 1201ProgramAlarm Sep 21 '17 at 18:16
  • @1201ProgramAlarm Yes, but what kind of magic is this? – vladon Sep 21 '17 at 18:18
  • 1
    I don't know. What C++ statement/assembly instructions follow what you've posted? – 1201ProgramAlarm Sep 21 '17 at 18:19
  • Anyway, you can disassemble with `objdump -drwC -Mintel` to show relocations and find out what symbol that `call` will resolve to on linking. – Peter Cordes Sep 21 '17 at 18:23
  • 1
    @PeterCordes Thanks. It's our internal code. It is really clang 3.9.1 with -O2. But if it is, then call 0x0 will be never executed. Why there is test and je over instruction? Hmmm... – vladon Sep 21 '17 at 18:26
  • @PeterCordes Yes, I will try to make minimal example, will come back here soon. – vladon Sep 21 '17 at 18:29
  • 2
    Is this GDB disassembly from a linked binary? Can you post `objdump` output instead, so we can see the machine-code bytes to be sure this is jumping to address zero, or to the next instruction. (Again, please confirm it's a linked binary, not an object file with placeholders). (Or just work on that test-case, since that would be much more useful.) – Peter Cordes Sep 21 '17 at 18:32
  • Have you tried a different compiler? Maybe your clang is broken / misconfigured (to not optimize? If that's possible?) – Peter Cordes Sep 21 '17 at 18:39
  • @PeterCordes will try tomorrow... – vladon Sep 21 '17 at 18:39
  • 1
    @PeterCordes machine code of `call 0x0` here is `e8 1a 2d c5 ff` – vladon Sep 21 '17 at 18:42
  • 1
    That's a `call` rel32 with exactly minus the next address, so it's a `call` to a null pointer. – Matteo Italia Sep 21 '17 at 19:06
  • @MatteoItalia anyway this will never be called – vladon Sep 21 '17 at 19:10
  • @vladon: I think I finally got it, see my answer. – Matteo Italia Sep 22 '17 at 23:15
  • `mov eax, 0x0` is a `mov eax, imm32`, zeroing the entire rax. https://stackoverflow.com/questions/11177137/why-do-most-x64-instructions-zero-the-upper-part-of-a-32-bit-register. GDB would disassemble an absolute load differently, and it's only 5 bytes so it's definitely a `mov eax, imm32`. I deleted some earlier comments, but have rechecked. See [comments on Matteo's answer](https://stackoverflow.com/questions/46350290/what-is-the-sense-of-this-asm-code/46350377?noredirect=1#comment79713049_46350377) – Peter Cordes Sep 23 '17 at 07:34

1 Answers1

3

Reading an unrelated question that exposes a pattern that looked similar to yours, I think I found the origin of your strange code.

Most probably this code comes from an expansion of thread-related C++11 functionality, in particular stuff that uses __gthread_active_p() to check if the pthread library was actually linked in.

Most code that has to potentially deal with threading issues in libstdc++ (but has reasonable fallbacks for when no threading support is required) is littered with

if(__gthread_active_p())

which in turn generally boils down to checking if the symbol __pthread_key_create is defined to something different than NULL. The trick here is that such a function is declared with __attribute__ ((weak)), so, if no definition is provided (i.e. if the pthread library is not linked in), instead of complaining, the linker just resolves its references to NULL.

So, what you are seeing in your code are the checks that the compiler left in to do thread-related stuff (say, acquiring a mutex that protects some shared resources). The pre-linking code is probably something like:

    mov eax,__pthread_key_create
    test rax,rax
    jz .skip_mutex_init
    call __pthread_init_some_mutex
.skip_mutex_init:
    mov rax, 0xffffffffffffff60 
    sub rbx,r15
    add QWORD PTR fs:[rax],rbx`

where you see all the __pthread symbols resolved to NULL pointers. Of course the resulting code is quite dumb - the compiler couldn't know if the linker was going to be invoked with -lpthread, and the linker just performs dumb substitutions - it's too late to run the optimizer again (unless LTO is enabled, but that's an entirely different game).

You can see yourself similar patterns playing with the compiler explorer: try enable/disable linking the full binary (the button on the right with 11010 caption), and adding/removing -pthread on the command line.

Now, I didn't manage to reproduce exactly your result, but probably that comes from a different version of libstdc++ being used, or different thread-aware components being used in your code (I just tried with std::mutex and std::shared_ptr).

Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
  • No, `call 0x0` is just calling next instruction, not a null pointer. It is a disasm of already statically linked program (output of gdb disasm). – vladon Sep 21 '17 at 18:00
  • I'm not sure, IIRC usually gdb dumps that as `call 0x3ad2e6 <+0>`, similar to the relative jump above. Still, if it's as you say it would look like handwritten code to stop decompilers and the like - inserting a fake call inside the function itself wreaks havoc into the heuristics to reconstruct the function entry points/blocks. – Matteo Italia Sep 21 '17 at 18:14
  • Oh crap, yeah, that was a NULL-pointer load, not `mov eax, imm32`. I'm just not used to GDB's version of Intel-syntax. `objdump` would have printed `DWORD PTR:0`. (@vladon) – Peter Cordes Sep 23 '17 at 06:12
  • I tested with gdb. Load of absolute 0 is `mov eax,DWORD PTR ds:0x0`, but `mov eax, imm32` is `mov eax,0x0`. Perhaps the original code was `mov eax, OFFSET __pthread_key_create`? – Peter Cordes Sep 23 '17 at 07:33
  • Yeah, a load immediate is what I meant. I don't really know about the `OFFSET` syntax, generally in `nasm` a `mov reg, label` is meant as an immediate load of the address of *label*, an indirect load would be `mov reg, [label]` (`DWORD PTR` is generally omitted if it can be inferred by the size of the target register). Sorry for the confusion. – Matteo Italia Sep 23 '17 at 14:21