dot_product.asm runtime problem
Existing issues
- [x] I have searched the existing issues.
Affected code example
https://github.com/0xAX/asm/blob/master/content/asm_6.md
Issue description
when i debug with gdb run,segmentation fault issue appears. then info registers
Actual vs expected result
register memory issue of dot product
Suggested improvements
No response
Attachments
No response
hello @frankchieng1982. I can not reproduce the issue:
~$ gdb ./dot_product
The target architecture is set to "i386:x86-64".
Reading symbols from ./dot_product...
(gdb) run
Starting program: /home/alex/dot_product
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Input the first vector: 2.0 3.0
Input the second vector:
Are you using the code 1:1 from https://github.com/0xAX/asm/blob/master/float/dot_product.asm and build instructions from the makefile near to this file or you changed something?
Can you setup breakpoint on the _parse_first_float_vector. The rdi should have pointer to the input right after _parse_first_float_vector entered:
~$ gdb ./dot_product
The target architecture is set to "i386:x86-64".
Reading symbols from ./dot_product...
(gdb) break _parse_first_float_vector
Breakpoint 1 at 0x400258: file test.nasm, line 114.
(gdb) run
Starting program: /home/alex/dot_product
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Input the first vector: 2.0 3.0
Breakpoint 1, _parse_first_float_vector () at test.nasm:114
114 mov rsi, end_buffer_1
Missing rpms, try: dnf --enablerepo='*debug*' install glibc-debuginfo-2.41-11.fc42.x86_64
(gdb) print $rdi
$1 = 4208336
(gdb) x/s $rdi
0x4036d0 <buffer_1>: "2.0 3.0\n"
yep,it's weird,exactly the same source code and compiled, but my cpu is AMD EPYC 7551P 32-Core Processor,not Intel .
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04.2) 12.1
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./dot_product...
(gdb) run
Starting program: /home/user/develop/assembly/asm/dot_product
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Input the first vector: 2.0 3.0
Program received signal SIGSEGV, Segmentation fault.
0x00000000004010b8 in _parse_first_float_vector ()
(gdb)
could you please setup breakpoint
(gdb) run
Starting program: /home/user/develop/assembly/asm/dot_product
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Input the first vector: 2.0 3.0
Program received signal SIGSEGV, Segmentation fault.
0x00000000004010b8 in _parse_first_float_vector ()
(gdb) disassemble _parse_first_float_vector
Dump of assembler code for function _parse_first_float_vector:
0x0000000000401088 <+0>: movabs $0x4056e8,%rsi
0x0000000000401092 <+10>: call 0x401010 <strtod@plt>
0x0000000000401097 <+15>: mov 0x4056e8,%rax
0x000000000040109f <+23>: cmp %rdi,%rax
0x00000000004010a2 <+26>: je 0x4010c4 <_read_second_float_vector>
0x00000000004010a4 <+28>: movabs $0x4030a8,%rdx
0x00000000004010ae <+38>: mov %r14,%rcx
0x00000000004010b1 <+41>: shl $0x3,%rcx
0x00000000004010b5 <+45>: add %rcx,%rdx
=> 0x00000000004010b8 <+48>: movq %xmm0,(%rdx)
0x00000000004010bc <+52>: inc %r14
0x00000000004010bf <+55>: mov %rax,%rdi
0x00000000004010c2 <+58>: jmp 0x401088 <_parse_first_float_vector>
End of assembler dump.
and the breakpoint details as below
Reading symbols from ./dot_product...
(gdb) br *0x00000000004010b8
Breakpoint 1 at 0x4010b8
(gdb) run
Starting program: /home/user/develop/assembly/asm/dot_product
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Input the first vector: 2.0 3.0
Breakpoint 1, 0x00000000004010b8 in _parse_first_float_vector ()
(gdb) stepi
0x00000000004010bc in _parse_first_float_vector ()
(gdb) x/gx $rdx
0x4030a8 <vector_1>: 0x4000000000000000
(gdb) stepi
0x00000000004010bf in _parse_first_float_vector ()
(gdb) x/gx $rdx
0x4030a8 <vector_1>: 0x4000000000000000
(gdb) stepi
0x00000000004010c2 in _parse_first_float_vector ()
(gdb) x/gx $rdx
0x4030a8 <vector_1>: 0x4000000000000000
(gdb) stepi
0x0000000000401088 in _parse_first_float_vector ()
(gdb) x/gx $rdx
0x4030a8 <vector_1>: 0x4000000000000000
(gdb) stepi
0x0000000000401092 in _parse_first_float_vector ()
(gdb) x/gx $rdx
0x4030a8 <vector_1>: 0x4000000000000000
(gdb) stepi
0x0000000000401010 in strtod@plt ()
(gdb) x/gx $rdx
0x4030a8 <vector_1>: 0x4000000000000000
(gdb) stepi
__GI_strtod (nptr=0x4036eb <buffer_1+3> " 3.0\n", endptr=0x4056e8 <end_buffer_1>) at ./stdlib/strtod.c:81
81 ./stdlib/strtod.c: No such file or directory.
(gdb) x/gx $rdx
0x4030a8 <vector_1>: 0x4000000000000000
(gdb) stepi
82 in ./stdlib/strtod.c
(gdb) x/gx $rdx
0x4030a8 <vector_1>: 0x4000000000000000
(gdb) stepi
0x00007ffff7847fab 82 in ./stdlib/strtod.c
(gdb) x/gx $rdx
0x4030a8 <vector_1>: 0x4000000000000000
(gdb) stepi
0x00007ffff7847fad 82 in ./stdlib/strtod.c
(gdb) x/gx $rdx
0x0: Cannot access memory at address 0x0
(gdb) stepi
0x00007ffff7847fad 82 in ./stdlib/strtod.c
(gdb) x/gx $rdx
0x0: Cannot access memory at address 0x0
This by itself should not be issue. As you will step deeper in the strtod implementation you will see the 3rd parameter which is rdx set to 0, so I assume you just at it:
Breakpoint 1, _parse_first_float_vector () at test.nasm:137
137 movq [rdx], xmm0
(gdb) x/gx $rdx
0x403098: 0x0000000000000000
(gdb) stepi
140 inc r14
(gdb) x/gx $rdx
0x403098: 0x4008000000000000
(gdb) stepi
143 mov rdi, rax
(gdb) stepi
145 jmp _parse_first_float_vector
(gdb) stepi
114 mov rsi, end_buffer_1
(gdb) x/gx $rdx
0x403098: 0x4008000000000000
(gdb) stepi
116 call strtod
(gdb) stepi
0x00000000004001e0 in strtod@plt ()
(gdb) stepi
__GI_strtod (nptr=0x4036d7 <buffer_1+7> "\n", endptr=0x4056d0 <end_buffer_1>) at strtod.c:81
81 {
(gdb) stepi
82 return INTERNAL(STRTOF_L) (nptr, endptr, 0, _NL_CURRENT_LOCALE);
(gdb) stepi
0x00007ffff7dd4f3b 82 return INTERNAL(STRTOF_L) (nptr, endptr, 0, _NL_CURRENT_LOCALE);
(gdb) stepi
0x00007ffff7dd4f3d 82 return INTERNAL(STRTOF_L) (nptr, endptr, 0, _NL_CURRENT_LOCALE);
(gdb) stepi
0x00007ffff7dd4f41 82 return INTERNAL(STRTOF_L) (nptr, endptr, 0, _NL_CURRENT_LOCALE);
(gdb) stepi
__GI_____strtod_l_internal (nptr=0x4036d7 <buffer_1+7> "\n", endptr=0x4056d0 <end_buffer_1>, group=0, loc=0x7ffff7f993c0 <_nl_global_locale>) at strtod_l.c:510
510 {
(gdb) stepi
0x00007ffff7dd56c4 510 {
(gdb) x/gx $rdx
0x0: Cannot access memory at address 0x0
(gdb)
Moreover, we re-initialize the rdx each time after strtod executes pointing to the vector_1. Can you add another label/breakpoint to the code:
;; Move the pointer from the beginning of the buffer with floating-point values that
;; we parsed from the input string to the next value.
add rdx, rcx
br:
;; Store the next floating-point value in the buffer.
movq [rdx], xmm0
And run the same commands in gdb:
~$ gdb ./dot_product
The target architecture is set to "i386:x86-64".
Reading symbols from ./dot_product...
(gdb) break br
Breakpoint 1 at 0x400288: file test.nasm, line 139.
(gdb) watch *(double*)&vector_1
Hardware watchpoint 2: *(double*)&vector_1
(gdb) run
Starting program: /home/alex/dot_product
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Input the first vector: 2.0 3.0
Breakpoint 1, br () at test.nasm:139
139 movq [rdx], xmm0
(gdb) c
Continuing.
Hardware watchpoint 2: *(double*)&vector_1
Old value = 0
New value = 2
br () at test.nasm:142
142 inc r14
(gdb) c
Continuing.
Breakpoint 1, br () at test.nasm:139
139 movq [rdx], xmm0
(gdb) x/20gf &vector_1
0x403090 <vector_1>: 2 0
0x4030a0: 0 0
0x4030b0: 0 0
0x4030c0: 0 0
0x4030d0: 0 0
0x4030e0: 0 0
0x4030f0: 0 0
0x403100: 0 0
0x403110: 0 0
0x403120: 0 0
(gdb) stepi
142 inc r14
(gdb) x/20gf &vector_1
0x403090 <vector_1>: 2 3
0x4030a0: 0 0
0x4030b0: 0 0
0x4030c0: 0 0
0x4030d0: 0 0
0x4030e0: 0 0
0x4030f0: 0 0
0x403100: 0 0
0x403110: 0 0
0x403120: 0 0
(gdb)
thanks for your suggestion, i've solved this issue(mainly the problem is stack misalignment) rely on LLM finally thr modifing the code
_parse_first_float_vector:
push rbp ; Save base pointer
mov rbp, rsp ; Set up stack frame
sub rsp, 8 ; Align stack to 16 bytes (call pushes 8 bytes)
the original asm source code compile error as below:
(gdb) break *(_parse_first_float_vector + 8)
Breakpoint 1 at 0x401090
(gdb) run
Starting program: /home/user/develop/assembly/asm/dot_product
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Input the first vector: 2.0 3.0
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff784bc0b in __GI_____strtod_l_internal (nptr=<optimized out>, endptr=0xcc0000004056e8, group=<optimized out>, loc=<optimized out>) at ./stdlib/strtod_l.c:1043
1043 ./stdlib/strtod_l.c: No such file or directory.
Crash Location: The segmentation fault occurs at 0x00007ffff784bc0b in __GI_____strtod_l_internal (the internal strtod function in libc), specifically at line 1043 of strtod_l.c. The address 0x7ffff784bc0b is in the shared library libc.so, indicating the crash is inside the C library. Crash Timing: The program crashes before reaching the breakpoint, meaning the segfault happens during the first call to strtod, likely due to invalid arguments or stack misalignment. the 64-bit System V AMD64 ABI requires the stack to be 16-byte aligned before calling C library functions like strtod. Misalignment can cause strtod to mishandle stack-based data or arguments, potentially corrupting %rsi or causing internal faults. The high bits in 0xcc0000004056e8 suggest stack corruption or register misuse. Why It Worked on i386:
In 32-bit mode (i386), the ABI uses %esi (32-bit) instead of %rsi (64-bit) for endptr. The lower 32 bits of end_buffer_1’s address (e.g., 0x4056e8) might be valid in the 32-bit address space, and the 32-bit strtod is less strict about pointer validation. The 32-bit ABI doesn’t require 16-byte stack alignment, so misalignment doesn’t cause crashes. The i386 system likely produced a 32-bit binary (despite -f elf64, due to environment constraints), matching the 32-bit libc and avoiding ABI issues.
Why It Fails on AMD EPYC:
The AMD EPYC system runs a 64-bit OS, producing a 64-bit binary with -f elf64 and linking against 64-bit libc. The 64-bit strtod expects %rsi to point to a valid, writable char** (i.e., end_buffer_1’s address, which should be in .bss, like 0x404xxx). If %rsi contains garbage (e.g., 0xcc0000004056e8), strtod attempts to write to this address, causing a segfault. Stack misalignment exacerbates the issue, as strtod may rely on aligned stack accesses for internal operations, leading to corruption or crashes.
Interesting. If endptr is passes via register why alignment of stack plays role 🤔
But thanks, since:
the 64-bit System V AMD64 ABI requires the stack to be 16-byte aligned before calling C library functions like strtod
I will try to update all examples that uses C stdlib
@frankchieng1982 can you setup breakpoint to _start and shows registers when it is catched? The AMD64 ABI says:
Only the registers listed below have specified values at process entry:
%rsp The stack pointer holds the address of the byte with lowest address which is part of the stack. It is guaranteed to be 16-byte aligned at process entry.
and btw what NASM version you are using?
NASM version 2.15.05,anyway, x86_64 requires the stack to be aligned to a 16-byte boundary before certain operations, like calling functions