1
# 0x00007f33caf5a85f: cmp rax, 0xffffffff80000000
# 0x00007f33caf5a865: jnl 0x7f33caf5a898
...
target_of_jnl:
# 0x00007f33caf5a898: cmp rax, 0x7fffffff
# 0x00007f33caf5a89e: jle 0x7f33caf5a8c8

The above code snip is part of an execution flow of function _M_extract_int() in libstdc++.

I don't understand the meaning of the two compares. I think 0xffffffff80000000 is the top physical memory address can be used for user mode. But how about 0x7fffffff? What are they checking?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
syacer
  • 157
  • 6

1 Answers1

3

Note that 0x...865 and 0x...898 are not contiguous, even though they're shown in the question as part of one contiguous block with no blank line. One is the branch target of the other.

It appears to be checking for a value that can fit in a 32-bit 2's complement integer, i.e. between INT32_MIN and INT32_MAX (inclusive). Like x == (int32_t)x

But the branch targets are different for high-half and low-half, otherwise it could simply movsxd rdx, eax / cmp rax, rdx / je.

jnl is the same condition as jge, so it's jumping on (int64_t)rax >= INT_MIN, with INT_MIN of course sign-extended to 64-bit.

   cmp rax, 0xffffffff80000000                   # INT32_MIN
   jge   x_ge_INT32_MIN
# else fall-through: x < INT32_MIN
  ... other code here

x_ge_INT32_MIN:
   cmp rax, 0x7fffffff                           # INT32_MAX
   jle   x_fits_in_int32_t

  ... else it doesn't, x > INT32_MAX

Note that 0xffffffff80000000 represents a negative signed integer, (int64_t)INT32_MIN. It's the sign-extension of 32-bit 0x80000000.


I don't know what _M_extract_int() does and you didn't link any libstdc++ source for usage info.


I think 0xffffffff80000000 is the top physical memory address can be used for user mode

No, 0xffffffff80000000 is in the high half of virtual address space, and thus not already too high. See Address canonical form and pointer arithmetic for a diagram of x86-64 48-bit canonical virtual address-space.

Linux (and I think all mainstream x86-64 OSes) reserve the entire top half for the kernel's use, with user-space able to allocate / map pages in the low half. i.e. user-space can use the entire low 47 bits of virtual address space. (Maybe not the very bottom, e.g. Linux stops processes from mapping the low 64k by default, so nullptr dereferences, even with an offset, will still fault. MacOS reserves the entire low 4GiB.)

With 5-level page tables (Intel's PML5 extension), user-space can use the low 56 bits of the 57-bit virtual address space.

Either way, this is a similar thing to what this code is looking for, but with 48 or 57-bit values that are correctly sign-extended to 64-bit, not 32.

physical

nope. libstdc++ is used in user-space only, so the only addresses it will ever see are virtual.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • 2
    @syacer: I had a closer look at the branch condition and updated my answer. It's actually weird. Oh, I see from the addresses that the instructions aren't actually contiguous! A `...` or blank line between them would have been much better. – Peter Cordes Oct 26 '20 at 09:25
  • Hi Peter, Sorry for the confusion. This is the executed instruction trace dumped by Intel Pin Tool. It is not the source code. That is why the instruction addresses are not contiguous. Thanks! – syacer Oct 26 '20 at 10:11
  • @syacer: It would have been helpful to show that like I did in an edit to the question. Now you know for next time. – Peter Cordes Oct 26 '20 at 10:49
  • I have no idea why this answer has 2 downvotes. 1 random downvote happens sometimes, but is there something I missed or got wrong? Downvoters please let me know. – Peter Cordes Oct 26 '20 at 10:51