This is the test application and I just picked 208 out of the blue, because I knew it would cause an error. I've done the test bed listing like this so you can see the correlation between error reporting and actual code.
_start
4001c0: c8 94 00 00 enter 0x94,0x0
4001c4: b0 ff mov al,0xff
4001c6: e8 f5 00 00 00 call InitFrame
4001cb: b0 d0 mov al,0xd0 io_getevents (I knew this would cause an error)
4001cd: 0f 05 syscall
4001cf: 77 0a ja Exit
4001d1: bf c0 01 40 00 mov edi,0x4001c0
4001d6: e8 e5 fe ff ff call FmtErr
.Exit
4001db: c9 leave
4001dc: b0 3c mov al,0x3c
4001de: 0f 05 syscall
This will produce an output like this and all values are in hex. Only errors that aren't trapped by application will show like this and the detail is of more meaning to developer than user.
Error [ 16 ] in 4001C0 + D
= EINVAL > Invalid argument
[A] Abort
[R] Retry
(I) Ignore:
[ ] <---- Operator enters selection here
This algo is peculiar to the way I do things and the offset make sense because I'll either convert that value in GDB or look for the entry point in the object listing
Main.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <_start>:
0: c8 94 00 00 enter 0x94,0x0
4: b0 ff mov al,0xff
6: e8 00 00 00 00 call b <_start+0xb>
b: b0 d0 mov al,0xd0
d: 0f 05 syscall
f: 77 0a ja 1b <_start.Exit>
11: bf 00 00 00 00 mov edi,0x0
16: e8 00 00 00 00 call 1b <_start.Exit>
000000000000001b <_start.Exit>:
1b: c9 leave
1c: b0 3c mov al,0x3c
1e: 0f 05 syscall
00: 53 push rbx
01: c8 80 00 00 enter 0x80, 0x0
Stack must be populated with data so string can be constructed with these details
#5: Pointer to description string of error
#4: How long is the description string
#3: Offset within function where error occured
#2: Callers entry point ( Can be referenced against a symbol table )
#1: Error code:
05: 48 f7 d8 neg rax Abs value of error
08: 49 89 c0 mov r8, rax
0b: ff c8 dec eax
0d: c1 e0 02 shl eax, 0x2
10: 05 00 00 00 00 add eax, PNTRS
15: 48 89 c6 mov rsi, rax
18: ad lods eax, [rsi]
19: 50 push rax #5 Length of description
1a: 67 2b 06 sub eax, [esi]
1d: f7 d8 neg eax
1f: 50 push rax #4 Address of description
Now we need calculate where from the entry point of callers routine, error occured by
searching backward for 05 and then if byte pointed to by RDI is 0F, we know we're at syscall
20: 57 push rdi
21: 48 8b 7d 10 mov rdi, [rbp+0x10]
25: 48 83 ef 06 sub rdi, 0x6
29: b0 05 mov al, 0x5
2b: 48 31 c9 xor rcx, rcx
2e: b1 ff mov cl, 0xff Should be closer than 256 bytes back.
30: fd std
31: f2 ae repnz scasb
33: fc cld
34: 67 80 3f 0f cmp [edi], 0xf
38: 74 10 je 4a
3a: 09 c9 or ecx, ecx
3c: 75 f2 jne 30
Programmer has done something wrong with calling sequence, because you shouldn't be calling
this procedure more than 256 bytes from SYSCALL.
3e: bf f0 ff ff ff mov edi, 0xfffffff0
43: 48 31 c0 xor rax, rax
46: b0 3c mov al, 0x3c
48: 0f 05 syscall
4a: 2b 3c 24 sub edi, [rsp]
4d: 48 87 3c 24 xchg [rsp], rdi #3 Offset in callers pace
51: 57 push rdi #2 Callers function entry point
52: 41 50 push r8 Local copy procedure trashes error #
Now we can begin moving these string segments and each time NULL is encountered this is
where one value will be poped from stack and put in that position
FmtStr db ' Error [ ', 0
db ' ] in ', 0
db ' + ', 0
db 10, 10, 9, ' = ', 0, 10
Last db 10, 9, 9, '[A] Abort '
db 10, 9, 9, '[R] Retry '
db 10, 9, 9, '[I] Ignore:'
db 10, 9, 9, '[ ]', 8, 8, 0
FmtStrSz equ $ - Last
54: be 00 00 00 00 mov esi, FmtStr
59: 48 8d 7d 80 lea rdi, [rbp-0x80] Destination buffer on stack
5d: bb 00 00 00 00 mov ebx, 0x0
62: b9 03 00 00 00 mov ecx, 0x3
Move first four segments
67: ff d3 call rbx Copy to destination
69: 58 pop rax Get next value
6a: e8 00 00 00 00 call I2A Convert to hex value
6f: 48 ff cf dec rdi Bounce back over NULL
72: e2 f3 loop 67
74: ff d3 call rbx
76: 59 pop rcx Length of decription string
77: 48 87 34 24 xchg [rsp], rsi
7b: f3 a4 rep movsb Move description string
7d: 5e pop rsi
7e: b9 31 00 00 00 mov ecx, 0x31
83: f3 a4 rep movsb Move everything from Last->FmtStrSz
Prompt string is completely constructed and ready to show on screen
85: 48 8d 75 80 lea rsi, [rbp-0x80]
89: 56 push rsi
8a: e8 00 00 00 00 call Show Display constructed string
8f: 48 83 c4 08 add rsp, 0x8
Get single key response from operator in non-canonical mode
93: e8 00 00 00 00 call Ask Get operators selection
98: 24 5f and al, 0x5f Convert to uppercase
9a: 3c 41 cmp al, 0x41 Was selection "A"
9c: 75 08 jne a6
Operator chose Abort, bail from app
9e: 48 83 cf ff or rdi, 0xffffffffffffffff
a2: b0 3c mov al, 0x3c
a4: 0f 05 syscall
Here we test for "I" or "R", it must be one or else just keep cycling
a6: 3c 49 cmp al, 0x49 Was selection "I"
a8: 74 06 je b0 ZR = 1 ignore error
aa: 3c 52 cmp al, 0x52 Was selection "R"
ac: 75 e5 jne 93 Do again if none of A, I or R
ae: fe c0 inc al ZR = 0 retry function
We are done
b0: c9 leave
b1: 5b pop rbx
b2: c3 ret
Local subroutine to copy source -> destination until NULL
b3: aa stosb
b4: ac lodsb
b5: 3c 00 cmp al, 0x0
b7: 75 fa jne b3
b9: c3 ret