Dave's nasmjf Dev Log 03
Created: 2022-07-22
This is an entry in my developer’s log series written between December 2021 and August 2022 (started project in September). I wrote these as I completed my port of the JONESFORTH assembly language Forth interpreter.
Picking up at the error discovered in the last log,
we need to figure out what's happening to the string
stored at the label 'errmsg'.
(I keep having to re-learn that GBD needs an '&' before
a label for it to be used as an address expression,
otherwise it uses the value _at_ that label. Hope I remember
it this time after fumbling around for a while...which is
not pictured here.)
The string is correct ("PARSE ERROR: ") when the program starts.
(Though it looks like I didn't get the newline escape right
in NASM because I have a literal '\n' in my string!"
Reading symbols from nasmjf...
(gdb) info addr errmsg
Symbol "errmsg" is at 0x804a315 in a file compiled without debugging.
(gdb) x/20c &errmsg
0x804a315 <errmsg>: 80 'P' 65 'A' 82 'R' 83 'S' 69 'E' 32 ' ' 69 'E' 82 'R'
0x804a31d: 82 'R' 79 'O' 82 'R' 58 ':' 32 ' ' 92 '\\' 110 'n' 0 '\000'
0x804a325: 0 '\000' 0 '\000' 0 '\000' 0 '\000'
I set a watchpoint on the label and run it.
(gdb) watch (int)errmsg
Watchpoint 2: (int)errmsg
(gdb) c
Continuing.
foo
Watchpoint 2: (int)errmsg
Old value = 1397899600
New value = 1392508928
code_INTERPRET () at nasmjf.asm:214
214 call _FIND ; Returns %eax = pointer to header or 0 if not found.
(gdb)
Okay, so I guess that means that the line _before_ 214 must
be the culprit. Ah, yeah. That looks likely because here's
how the memory is reserved:
interpret_is_lit: db 0 ; 1 means "reading a literal"
errmsg: db "PARSE ERROR: "
And here's the line before 214:
mov [interpret_is_lit], eax ; 0 means not a literal number (yet)
Yup, that sure makes sense! We reserved a byte for the
interpret_is_lit flag, but eax is a full word (4 bytes),
so the flag is getting set _and_ the remaining three
bytes clobber the string in errmsg. It's things like this that
make a person appreciate the safety features of higher
level languages.
Angway, the data size was "int" in the JonesForth GAS original.
So mine should be 'w' for word:
interpret_is_lit: dw 0 ; 1 means "reading a literal"
Let's try it:
(gdb) file nasmjf
Reading symbols from nasmjf...
(gdb) break 271
Note: breakpoints 3, 4 and 5 also set at pc 0x80490ba.
Breakpoint 6 at 0x80490ba: file nasmjf.asm, line 271.
(gdb) r
Starting program: /home/dave/nasmjf/nasmjf
foo
Breakpoint 3, code_INTERPRET.parse_error () at nasmjf.asm:271
271 int 80h
PARSE ERROR:
Yay, that's restored the PARSE ERROR string.
Unfortunately, it still ends in a segfault.
Oops! Both the errmsgnl and __NR_write labels
should be the addresses, not the values at the
addresses at lines 284 and 286.
273 mov [currkey],ecx ; the error occurred just before currkey position
274 mov edx,ecx
275 sub edx,buffer ; edx = currkey - buffer (length in buffer before currkey)
276 cmp edx,40 ; if > 40, then print only 40 characters
277 jle .print_error
code_INTERPRET.print_error () at nasmjf.asm:280
280 sub ecx,edx ; ecx = start of area to print, edx = length
281 mov eax,__NR_write ; write syscall
282 int 80h
284 mov ecx,[errmsgnl] ; newline
285 mov edx,1 ; 1 char long
286 mov eax,[__NR_write] ; write syscall
Program received signal SIGSEGV, Segmentation fault.
Okay, easy fix. I also noticed I stll had the operands
transposed on line 273. And I swapped out the double
quotes around the errmsgnl string literal to backquotes
as required by NASM for backslash escape support.
Let's see how it goes now:
Continuing.
foo
PARSE ERROR: foo
[Inferior 1 (process 2582) exited normally]
Yay!