Debugging on the AMD64 Platform: Finding Argument ValuesPoonam Bajaj, July 2008 IntroductionThis article discusses the parameter passing convention used on the AMD64 platform, why this convention is used, and how it can make debugging and finding the values of arguments passed during function calls very difficult. We look at the register usage and calling conventions on the Solaris OS, Linux, and Microsoft Windows, and in Java technology. An example workaround is provided for AMD64 architecture on Linux. This article covers the following topics:
Before going into the debugging details, let's have a look at the AMD64 architecture and how it is different from the 32-bit x86 architecture. AMD64 ArchitectureThe AMD64 architecture has sixteen 64-bit general-purpose registers: The The AMD64 architecture also provides sixteen 128-bit XMM registers and eight x87 floating point registers. Argument Passing in x86 ArchitectureLet's first see the stack organization and how parameters are passed in a 32-bit x86 architecture. In the x86 architecture, parameters are passed on the stack. When a function calls another function, first all the parameters are pushed, one after another from right to left, onto the stack. Then the address of the instruction after the "call" instruction, which is the return address for the call, is pushed. At this point, the caller loses control and the callee gets control. The called function saves the
In the previous stack depiction, the stack grows downwards. Using Registers for Passing Arguments on the AMD64 PlatformSince the AMD64 architecture has eight new general-purpose registers, as a performance enhancement, it was decided to use registers, wherever possible, to pass arguments to functions. The AMD64 architecture defines a number of classes to classify arguments, such as
The first six arguments are passed in registers and the rest on the stack.
Here is additional architecture-specific information about register usage. Register usage for Microsoft Windows: On the Microsoft Windows AMD64 platform, there are only four Register usage by Java Native Interface (JNI) methods: In Java technology, in an attempt to do less shuffling of arguments, the calling convention of JNI methods is a shifted version of the C convention. On the Solaris OS and Linux, the argument register order while calling JNI methods is On Microsoft Windows, the argument register order while calling JNI is Now, these argument registers are not callee-saved and are not guaranteed to have the argument values when we look at them in the called function. This can make it very, very difficult to get the argument values. WorkaroundHere is how I got to the argument values while working on a core file from a 1.5.0_14 HotSpot Server Java Virtual Machine (JVM) crash on a Linux machine built on the AMD64 platform. The stack trace of the crash is as follows: (gdb) bt #0 0x00000031b8b2e21d in raise () #1 0x00000031b8b2fa1e in abort () #2 0x0000002a95ad5372 in os::abort () #3 0x0000002a95bcfaa0 in VMError::report_and_die () #4 0x0000002a9584ea66 in report_fatal () #5 0x0000002a95ac41d8 in nmethod::continuation_for_implicit_exception () #6 0x0000002a95b41499 in SharedRuntime::continuation_for_implicit_exception () #7 0x0000002a95ad9726 in JVM_handle_linux_signal () #8 0x0000002a95ad716e in signalHandler () #9 <signal handler called> #10 0x0000002a99d01d2c in ?? () #11 0x0000000047b85b68 in ?? () #12 0x0000000047b85b28 in ?? () #13 0x0000002abdb30b7a in ?? () #14 0x0000000047b85b68 in ?? () #15 0x0000002a9f800b28 in ?? () #16 0x0000000000000000 in ?? () For this crash, I wanted to see what code was executed that caused this fatal error. To do that, I wanted to get the address nmethod::continuation_for_implicit_exception(address pc) In frame 5, the first argument is Looking at the instructions of the caller of frame 5, I saw the following:
(gdb) x/10i 0x2a95b41499-20
0x2a95b41485 <_ZN13SharedRuntime35continuation_for_implicit_exception
EP10JavaThreadPhNS_21ImplicitExceptionKindE+837>: cmp %rcx,%r10
0x2a95b41488 <_ZN13SharedRuntime35continuation_for_implicit_exception
EP10JavaThreadPhNS_21ImplicitExceptionKindE+840>:
jl 0x2a95b41373 <_ZN13SharedRuntime35continuation_for_implicit_exception
EP10JavaThreadPhNS_21ImplicitExceptionKindE+563>
0x2a95b4148e <_ZN13SharedRuntime35continuation_for_implicit_exception
EP10JavaThreadPhNS_21ImplicitExceptionKindE+846>: :mov %r13,%rdi
0x2a95b41491 <_ZN13SharedRuntime35continuation_for_implicit_exception
EP10JavaThreadPhNS_21ImplicitExceptionKindE+849>: mov %rbx,%rsi
0x2a95b41494 <_ZN13SharedRuntime35continuation_for_implicit_exception
EP10JavaThreadPhNS_21ImplicitExceptionKindE+852>:
callq 0x2a95ac4160 <_ZN7nmethod35continuation_for_implicit_exceptionEPh>
0x2a95b41499 <_ZN13SharedRuntime35continuation_for_implicit_exception
EP10JavaThreadPhNS_21ImplicitExceptionKindE+857>: test %rax,%rax
Here, note that before the call instruction, the two arguments were moved into
the (gdb) info registers rax 0x0 0 rbx 0x2a99d01d2c 182969179436 rcx 0xffffffffffffffff -1 rdx 0x6 6 rsi 0x7800 30720 rdi 0x7767 30567 rbp 0x47b854a0 0x47b854a0 rsp 0x47b85470 0x47b85470 r8 0x6 6 r9 0x7800 30720 r10 0x8 8 r11 0x202 514 r12 0x0 0 r13 0x2a99d01ad0 182969178832 r14 0x2a99d01d2c 182969179436 r15 0x2ac9081190 183761375632 rip 0x2a95b41499 0x2a95b41499 <SharedRuntime::continuation_for_implicit_exception(JavaThread*, unsigned char*,SharedRuntime::ImplicitExceptionKind)+857> eflags 0x202 [ IF ] ....... Now, looking at the value in (gdb) x/8x 0x2a99d01ad0 0x2a99d01ad0: 0x0000002a95dd2770 0x0000002a95c12055 0x2a99d01ae0: 0x00000108000013d8 0x000001d0000000c0 0x2a99d01af0: 0x0000121800000a38 0x0000000c00000038 0x2a99d01b00: 0x00000000fffffffe 0x0000002acb2a6190 And, we also see this: (gdb) x/6x 0x0000002a95dd2770 0x2a95dd2770 <_ZTV7nmethod+16>: 0x0000002a95797060 0x0000002a95ac4410 0x2a95dd2780 <_ZTV7nmethod+32>: 0x0000002a95797080 0x0000002a95797090 0x2a95dd2790 <_ZTV7nmethod+48>: 0x0000002a957970a0 0x0000002a957970b0 This confirms that it is A rip of frame 10 confirmed that (gdb) frame 10 #10 0x0000002a99d01d2c in ?? () (gdb) info registers ..... rip 0x2a99d01d2c 0x2a99d01d2c ..... In this case, I was lucky to get the arguments from the callee-saved registers that contained the argument values
before these were moved to the argument registers. But if these registers ( In such a case, from the called function's instructions, we should find where those callee-saved registers are saved on the stack, and
then the arguments can be accessed using the SummarySo, here's a summary of the steps followed to find argument values on AMD64 platform:
For More InformationHere are additional resources:
Comments (latest comments first)Discuss and comment on this resource in the BigAdmin Wiki
Unless otherwise licensed, code in all technical manuals herein (including articles, FAQs, samples) is provided under this License. |
BigAdmin SubscriptionsBigAdmin Areas
BigAdmin Sun Center
BigAdmin Topics | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||