1 ;; Native x86 GNU/Linux Forth System, Direct Threaded Code
2 ;; Forth segfault handler
4 ;; Copyright (C) 2020 Ketmar Dark // Invisible Vector
6 ;; This program is free software: you can redistribute it and/or modify
7 ;; it under the terms of the GNU General Public License as published by
8 ;; the Free Software Foundation, version 3 of the License ONLY.
10 ;; This program is distributed in the hope that it will be useful,
11 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ;; GNU General Public License for more details.
15 ;; You should have received a copy of the GNU General Public License
16 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
19 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
20 urfsegfault_output_fd: dd 2
22 urfsegfault_dump_ddepth: dd 16 ; default depth of the data stack dump-1
23 urfsegfault_dump_rdepth: dd 24 ; default depth of the data stack dump-1
25 urfsegfault_dbg_retaddr: dd 0
26 urfsegfault_reg_eax: dd 0
27 urfsegfault_reg_ebx: dd 0
28 urfsegfault_reg_ecx: dd 0
29 urfsegfault_reg_edx: dd 0
30 urfsegfault_reg_esi: dd 0
31 urfsegfault_reg_edi: dd 0
32 urfsegfault_reg_ebp: dd 0
33 urfsegfault_reg_esp: dd 0
34 urfsegfault_reg_flags: dd 0
37 urfsegfault_stack_bottom: dd 0
40 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
41 macro urfsegfault_save_all_registers_no_eax_f
{
50 macro urfsegfault_restore_all_registers_no_eax_f
{
60 macro urfsegfault_save_all_registers
{
65 macro urfsegfault_restore_all_registers
{
71 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
73 ;; all registers are preserved, including flags
76 urfsegfault_save_all_registers
77 push eax ; we will write from here
79 ld
ebx,[urfsegfault_output_fd
]
84 urfsegfault_restore_all_registers
88 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
89 ;; print string from ECX, length in EDX
90 ;; all registers are preserved, including flags
92 urfsegfault_emit_str_ecx_edx:
93 urfsegfault_save_all_registers
95 ld
ebx,[urfsegfault_output_fd
]
97 urfsegfault_restore_all_registers
101 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
102 ;; print string from ECX, length in EDX
103 ;; all registers are preserved, including flags
105 urfsegfault_emit_str_ecx_edx_safe:
106 urfsegfault_save_all_registers
115 call urfsegfault_emit_al
120 urfsegfault_restore_all_registers
124 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
126 ;; all registers are preserved, including flags
128 macro urfsegfault_emit char
{
130 call urfsegfault_emit_al
134 call urfsegfault_emit_al
139 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
141 ;; all registers are preserved, including flags
143 macro urfsegfault_cr
{
147 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
149 ;; string data immediately follows the code
150 ;; all registers are preserved, including flags
152 macro urfsegfault_printstr
[str] {
164 ld ecx,nstart ; address
165 ld edx,nend-nstart ; length
166 call urfsegfault_emit_str_ecx_edx
171 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
172 ;; print string with a newline
173 ;; string data immediately follows the code
174 ;; all registers are preserved, including flags
176 macro urfsegfault_printstrnl
[str] {
186 urfsegfault_save_all_registers
188 ld ebx,[urfsegfault_output_fd]
189 ld ecx,nstart ; address
190 ld edx,nend-nstart ; length
192 urfsegfault_restore_all_registers
197 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
199 ;; all registers are preserved, including flags
201 urfsegfault_emit_hex_al:
207 call urfsegfault_emit_al
210 call urfsegfault_emit_al
216 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
218 ;; all registers are preserved, including flags
220 urfsegfault_emit_hex_ax:
225 call urfsegfault_emit_hex_al
227 call urfsegfault_emit_hex_al
233 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
235 ;; all registers are preserved, including flags
237 urfsegfault_emit_hex_eax:
242 call urfsegfault_emit_hex_ax
244 call urfsegfault_emit_hex_ax
250 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
251 ;; print EAX as signed decimal
252 ;; all registers are preserved, including flags
253 ;; i could do this without divs, but meh
255 urfsegfault_emit_dec_eax:
256 urfsegfault_save_all_registers
263 urfsegfault_restore_all_registers
269 ; EAX: quotient; EDX: remainder
280 ; EDX: quotient; EAX: remainder
282 call urfsegfault_emit_al
286 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
288 ;; all registers are preserved, including flags
290 macro urfsegfault_printhex_byte bval
{
292 call urfsegfault_emit_hex_al
296 call urfsegfault_emit_hex_al
302 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
303 ;; print register in hex
304 ;; all registers are preserved, including flags
306 macro urfsegfault_printhex reg
{
308 call urfsegfault_emit_hex_eax
312 call urfsegfault_emit_hex_eax
318 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
319 ;; print register as signed decimal
320 ;; all registers are preserved, including flags
322 macro urfsegfault_printdec reg
{
324 call urfsegfault_emit_dec_eax
328 call urfsegfault_emit_dec_eax
334 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
335 ;; move to cfa from nfa
341 ;; other registers are preserved (except flags)
346 add ecx,4+1 ; lenflags, trailing length
352 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
353 ;; move to nfa from cfa
359 ;; other registers are preserved (except flags)
363 movzx ecx,byte [eax-1]
364 add ecx,4+1 ; lenflags, trailing length
370 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
372 ;; correctly handles the case when EAX == 0
377 ;; all registers are preserved (except flags)
379 urfsegfault_nfaprint:
389 add eax,4 ; skip length and flags
390 ld
ecx,eax ; starting address
391 call urfsegfault_emit_str_ecx_edx_safe
398 urfsegfault_printstr
"<???>"
402 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
403 ;; skip word argument
404 ;; correctly handles the case when EAX == 0
411 ;; ESI: (new) code pointer
412 ;; all registers are preserved (except flags)
414 urfsegfault_nfa_skip_arg:
420 movzx eax,byte [eax+1]
421 urfsegfault_nfa_skip_arg_type_in_eax:
434 ; cell-counted string?
439 inc esi ; skip trailing zero byte
457 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
458 ;; type word argument
459 ;; correctly handles the case when EAX == 0
465 ;; ESI: (new) code pointer
466 ;; all registers are preserved (except flags)
468 urfsegfault_nfa_print_arg:
482 call urfsegfault_emit_hex_eax
494 call urfsegfault_emit_dec_eax
497 ; cell-counted string?
500 urfsegfault_printstr
' "'
505 inc esi ;; skip trailing zero byte
506 call urfsegfault_emit_str_ecx_edx_safe
515 ;call urfsegfault_find_by_addr
516 call urfsegfault_cfa2nfa
517 call urfsegfault_nfaprint
523 urfsegfault_printstr
" {cblock}"
531 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
532 ;; find line for the PC
539 ;; other registers are preserved (except flags)
541 urfsegfault_find_pc_line_nfa:
547 ; load debug info address
551 ; load and check number of items
554 ; check number of items (just in case)
559 ; latest line we've seen will be in EDX
579 ; the following two will be set (ONLY!) by successfull call to `urfsegfault_find_by_addr`
580 urfsegfault_fba_nfa: dd 0
581 urfsegfault_fba_end: dd 0
583 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
584 ;; find which word owns the specified address
588 ;; ESI: vocptr (at latestptr)
591 ;; ZERO SET if EAX is 0
592 ;; other registers are preserved (except flags)
594 urfsegfault_find_by_addr_in_voc:
605 ; check for invalid sfa
617 ld
[urfsegfault_fba_nfa
],esi
618 ld
[urfsegfault_fba_end
],edx
621 or eax,eax ; fix zero flag
627 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
628 ;; find which word owns the specified address
629 ;; this assumes that all words are sequential in memory
635 ;; ZERO SET if EAX is 0
636 ;; other registers are preserved (except flags)
638 urfsegfault_find_by_addr:
642 ld
esi,[fvar_voclink_data
]
646 sub esi,4 ; move to latestptr
648 call urfsegfault_find_by_addr_in_voc
649 ld
esi,[esi+4] ; load next voclink
661 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
662 ;; print return stack
664 urfsegfault_cmd_printrstack:
665 ; print first 8 items, that should be enough
666 ; print return stack depth
667 urfsegfault_printstr
"=== RETURN STACK: "
668 ld
eax,[fvar_rp0_data
]
669 sub eax,[urfsegfault_reg_ebp
]
672 urfsegfault_printstrnl
" UNDERFLOWED ==="
674 sar eax,2 ; divide by cell
676 urfsegfault_printdec
eax
677 urfsegfault_printstrnl
" CELLS DEPTH ==="
679 urfsegfault_printstr
"**"
680 ld
eax,[urfsegfault_reg_esi
]
681 sub eax,4 ; because we just loaded it
682 call urfsegfault_find_by_addr
684 call urfsegfault_nfaprint
685 ; print line number if we know it
687 ld
eax,[urfsegfault_reg_esi
]
688 sub eax,4 ; because we just loaded it
689 call urfsegfault_find_pc_line_nfa
692 urfsegfault_printstr
" {"
693 call urfsegfault_emit_dec_eax
694 urfsegfault_printstr
"}"
699 ld
esi,[urfsegfault_reg_ebp
]
704 cp
ecx,[urfsegfault_dump_rdepth
]
706 ld
ecx,[urfsegfault_dump_rdepth
]
710 urfsegfault_printstr
" "
711 call urfsegfault_find_by_addr
712 push eax ; for line number
713 call urfsegfault_nfaprint
717 urfsegfault_printstr
" | "
718 call urfsegfault_emit_hex_eax
719 ; print decimal value
721 urfsegfault_printstr
" | "
722 call urfsegfault_emit_dec_eax
724 ; print line number if we know it
726 call urfsegfault_find_pc_line_nfa
729 urfsegfault_printstr
" {"
730 call urfsegfault_emit_dec_eax
731 urfsegfault_printstr
"}"
742 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
745 urfsegfault_cmd_printdstack:
746 ; print first 8 items, that should be enough
747 ; also, print them in backwards order (i.e. TOS will be printed last)
748 ; print data stack depth
749 urfsegfault_printstr
"=== DATA STACK: "
750 ld
eax,[fvar_sp0_data
]
751 sub eax,[urfsegfault_reg_esp
]
754 urfsegfault_printstrnl
" UNDERFLOWED ==="
757 sar eax,2 ; divide by cell
759 urfsegfault_printdec
eax
760 urfsegfault_printstrnl
" CELLS DEPTH ==="
763 jr z
,urfsegfault_cmd_printdstack_quit0
765 urfsegfault_printstr
"ESP="
766 ld
eax,[urfsegfault_reg_esp
]
767 urfsegfault_printhex
eax
771 jr z
,urfsegfault_cmd_printdstack_tos
772 cp
ecx,[urfsegfault_dump_ddepth
]
774 ld
ecx,[urfsegfault_dump_ddepth
]
778 ld
esi,[urfsegfault_reg_esp
]
787 urfsegfault_printstr
" "
788 urfsegfault_printhex
esi
789 urfsegfault_printstr
": "
790 urfsegfault_printhex
eax
791 urfsegfault_printstr
" | "
792 urfsegfault_printdec
eax
797 urfsegfault_cmd_printdstack_tos:
799 urfsegfault_printstr
" "
800 urfsegfault_printstr
" TOS "
801 urfsegfault_printstr
": "
802 urfsegfault_printhex
[urfsegfault_reg_ecx
]
803 urfsegfault_printstr
" | "
804 urfsegfault_printdec
[urfsegfault_reg_ecx
]
807 urfsegfault_cmd_printdstack_quit0:
811 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
812 ;; debugger segfault handler
815 urfd_sigact.
sa_handler: ;dd 0 ; union with .sa_sigaction
816 urfd_sigact.
sa_sigaction: dd urfsegfault_segfault_action
817 urfd_sigact.
sa_mask: dd 0 ;0xffffffff
818 urfd_sigact.
sa_flags: dd 4 ; SA_SIGINFO
819 urfd_sigact.
sa_restorer: dd 0
833 ._sigfault: ;sigfault
883 .uc_stack sigaltstack
884 .uc_mcontext sigcontext
896 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
897 urfsegfault_segfault_action:
899 ; switch to our own stack
900 ;ld eax,[fvar_dp_data]
902 ld
eax,[urfsegfault_stack_bottom
]
905 ; signum : dword [ebp+4]
906 ; siginfoptr : dword [ebp+8]
907 ; ucontextptr: dword [ebp+12]
909 ; copy registers from context to debugger data structures
911 ld
eax,[esi+ucshit.uc_mcontext.
eip]
912 ld
[urfsegfault_dbg_retaddr
],eax
913 ld
eax,[esi+ucshit.uc_mcontext.
eax]
914 ld
[urfsegfault_reg_eax
],eax
915 ld
eax,[esi+ucshit.uc_mcontext.
ebx]
916 ld
[urfsegfault_reg_ebx
],eax
917 ld
eax,[esi+ucshit.uc_mcontext.
ecx]
918 ld
[urfsegfault_reg_ecx
],eax
919 ld
eax,[esi+ucshit.uc_mcontext.
edx]
920 ld
[urfsegfault_reg_edx
],eax
921 ld
eax,[esi+ucshit.uc_mcontext.
esi]
922 ld
[urfsegfault_reg_esi
],eax
923 ld
eax,[esi+ucshit.uc_mcontext.
edi]
924 ld
[urfsegfault_reg_edi
],eax
925 ld
eax,[esi+ucshit.uc_mcontext.
ebp]
926 ld
[urfsegfault_reg_ebp
],eax
927 ld
eax,[esi+ucshit.uc_mcontext.
esp]
928 ld
[urfsegfault_reg_esp
],eax
929 ld
eax,[esi+ucshit.uc_mcontext.eflags
]
930 ld
[urfsegfault_reg_flags
],eax
933 urfsegfault_printstr
"***TRAP: SEGFAULT!"
938 ld
eax,[esi+ucshit.uc_mcontext.
eip]
939 urfsegfault_printstr
" EIP="
940 urfsegfault_printhex
eax
942 ld
eax,[esi+ucshit.uc_mcontext.
esp]
943 urfsegfault_printstr
" ESP="
944 urfsegfault_printhex
eax
946 ld
eax,[esi+ucshit.uc_mcontext.
ebp]
947 urfsegfault_printstr
" EBP="
948 urfsegfault_printhex
eax
950 ld
eax,[esi+ucshit.uc_mcontext.
esi]
951 urfsegfault_printstr
" ESI="
952 urfsegfault_printhex
eax
956 ld
eax,[esi+ucshit.uc_mcontext.
eip]
957 call urfsegfault_find_by_addr
960 urfsegfault_printstr
"*** CURRENT WORD: "
961 call urfsegfault_nfaprint
962 ; print line number if we know it
964 ld
eax,[esi+ucshit.uc_mcontext.
eip]
965 call urfsegfault_find_pc_line_nfa
968 urfsegfault_printstr
" {"
969 call urfsegfault_emit_dec_eax
970 urfsegfault_printstr
"}"
974 call urfsegfault_cmd_printdstack
975 call urfsegfault_cmd_printrstack
982 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
983 ;; called by the startup code
985 urforth_setup_segfault_handler:
986 urfsegfault_save_all_registers
987 ; set segfault hanlder
992 xor edx,edx ; ignore old info
996 urfsegfault_printstrnl
"TRAP: cannot setup segfault handler."
998 urfsegfault_restore_all_registers