RFR: 8302384: Handle hsdis out-of-bound logic for RISC-V

Ludovic Henry luhenry at openjdk.org
Fri Feb 17 16:21:28 UTC 2023


On Tue, 14 Feb 2023 07:10:42 GMT, Xiaolin Zheng <xlinzheng at openjdk.org> wrote:

> Several debug assertion failures have been observed on RISC-V, on physical boards only.
> 
> Failure list: (the `hs_err` log is in the JBS issue)
> 
> compiler/vectorapi/TestVectorShiftImm.java
> compiler/compilercontrol/jcmd/AddPrintAssemblyTest.java
> compiler/intrinsics/math/TestFpMinMaxIntrinsics.java
> compiler/compilercontrol/TestCompilerDirectivesCompatibilityFlag.java
> compiler/compilercontrol/TestCompilerDirectivesCompatibilityCommandOn.java
> compiler/runtime/TestConstantsInError.java
> compiler/compilercontrol/jcmd/PrintDirectivesTest.java
> 
> 
> When the failure occurs, hsdis is disassembling the last unrecognizable data at the end of a code blob, usually the data stored in trampolines. It could be theoretically any address inside the code cache, and sometimes binutils can recognize the data as 2-byte instructions, 4-byte instructions, and 6 or 8-byte instructions even though as far as I know no instructions longer than 4-byte have landed. Therefore, binutils may firstly run out of bound after the calculation. However, the RISC-V binutils returns a `EIO` [1] (which is the `status`, always a `5`, FYI), rather than returning a `-1` (FYI, [2][3][4][5]) on other platforms when such out-of-bound happens. So when coming back to our hsdis, we (hsdis) get the `size = 5` as the return value [6] rather than `-1`: our hsdis error handling is skipped, our variable `p` is out of bound, and then we meet the crash.
> 
> To fix it, we should check the value is the special `EIO` on RISC-V. However, after fixing that issue, I found binutils would print some messages like "Address 0x%s is out of bounds." on the screen:
> 
> 
> 0x0000003f901a41b4:   auipc	t0,0x0                      ;   {trampoline_stub}
> 0x0000003f901a41b8:   ld	t0,12(t0) # 0x0000003f901a41c0
> 0x0000003f901a41bc:   jr	t0
> 0x0000003f901a41c0:   .2byte	0x8ec0
> 0x0000003f901a41c2:   srli	s0,s0,0x21
> 0x0000003f901a41c4:   Address 0x0000003f901a41c9 is out of bounds.      <----------- But we want the real bytes here.
> 
> 
> So, we should overwrite the `disassemble_info.memory_error_func` in the binutils callback [7], to generate our own output:
> 
> 0x0000003f901a41b4:   auipc	t0,0x0                      ;   {trampoline_stub}
> 0x0000003f901a41b8:   ld	t0,12(t0) # 0x0000003f901a41c0
> 0x0000003f901a41bc:   jr	t0
> 0x0000003f901a41c0:   .2byte	0x8ec0
> 0x0000003f901a41c2:   srli	s0,s0,0x21
> 0x0000003f901a41c4:   .4byte	0x0000003f
> 
> 
> Mirroring the code of hsdis-llvm, to print merely a 4-byte data [8].
> 
> 
> BTW, the reason why the crash only happens on the physical board, is that boards support RISC-V sv39 address mode only: a legal user-space address can be no more than 38-bit. So the code cache is always mmapped to an address like `0x3fe0000000`. Such a `0x3f` is always recognized as the mark of an 8-byte instruction [9].
> 
> 
> Tested hotspot tier1~4 with fastdebug build, no new errors found.
> 
> Thanks,
> Xiaolin
> 
> 
> [1] https://github.com/bminor/binutils-gdb/blob/binutils-2_38-branch/opcodes/riscv-dis.c#L940
> [2] https://github.com/bminor/binutils-gdb/blob/binutils-2_38-branch/opcodes/aarch64-dis.c#L3792
> [3] https://github.com/bminor/binutils-gdb/blob/binutils-2_38-branch/opcodes/ppc-dis.c#L872
> [4] https://github.com/bminor/binutils-gdb/blob/binutils-2_38-branch/opcodes/s390-dis.c#L305
> [5] https://github.com/bminor/binutils-gdb/blob/binutils-2_38-branch/opcodes/i386-dis.c#L9466 (the i386 one uses a `setlongjmp` to handle the exception case, so the code might look different)
> [6] https://github.com/openjdk/jdk/blob/94e7cc8587356988e713d23d1653bdd5c43fb3f1/src/utils/hsdis/binutils/hsdis-binutils.c#L198
> [7] https://github.com/bminor/binutils-gdb/blob/binutils-2_38-branch/opcodes/dis-buf.c#L51-L72
> [8] https://github.com/openjdk/jdk/blob/94e7cc8587356988e713d23d1653bdd5c43fb3f1/src/utils/hsdis/llvm/hsdis-llvm.cpp#L316-L317
> [9] https://github.com/bminor/binutils-gdb/blob/binutils-2_38-branch/include/opcode/riscv.h#L30-L42

Changes requested by luhenry (Committer).

src/utils/hsdis/binutils/hsdis-binutils.c line 204:

> 202:       int size = (*app_data->dfn)((bfd_vma) p, &app_data->dinfo);
> 203: 
> 204:       if (size <= 0 RISCV_ONLY(|| size == EIO)) {

That should be fixed in binutils as well, or at least reported.

src/utils/hsdis/binutils/hsdis-binutils.c line 302:

> 300:   DECL_APP_DATA(dinfo);
> 301:   DECL_PRINTF_CALLBACK(app_data);
> 302:   (*printf_callback)(printf_stream, ".4byte\t0x%08x\n", *(uint32_t*)addr);

AFAIU, once this callback is called, we are not going to continue disassembling the code. Given we know how long the rest of the code to disassemble is (with `addr `, `app_data->start_va` and `app_data->length`), could we also print the content of the rest of the buffer without disassembling (with some `.4byte ..`, `.2byte ..`, and `.1byte ..`).

src/utils/hsdis/binutils/hsdis-binutils.c line 366:

> 364:   app_data->dinfo.print_address_func = hsdis_print_address_func;
> 365:   app_data->dinfo.read_memory_func = hsdis_read_memory_func;
> 366: #ifdef LIBARCH_riscv64

Instead of having a riscv64 specific callback, I would rather install this callback on all platforms and special case in the callback itself. (See the comment in the callback as well).

-------------

PR: https://git.openjdk.org/jdk/pull/12551


More information about the hotspot-compiler-dev mailing list