在Linux下,我找不到关于如何正确实现x86程序集中的系统调用错误处理的明确答案。
Shows,从系统调用返回的寄存器rax
包含系统调用的结果。 -4095到-1之间的值表示错误。
从逻辑上讲,如果我们“查看” rax
中的返回值,并且该值在此范围内,则可以得出结论:发生了错误,并且可以对该信息采取适当措施。
但是,我们如何知道返回值确实是负值?这实际上是我提出问题的基础,因为我的理解是,如果我们这样对待给定的二进制模式,则其值仅是负值。
例如,为便于说明,我们假设系统调用的返回值为“ -4000”。现在,传递给rax
的实际返回值实际上不是-4000,而是可以这样解释的二进制模式。在从系统调用解释返回值的情况下,我们如何区分这种二进制模式的一种可能解释和另一种解释?换句话说,我们如何知道返回值(二进制模式)不代表无符号等效项?
诚然,这将是一个相对较大的数字。但是,这不是合理的情况吗?毕竟,rax
仅包含位,我们是否将这些位视为代表负数取决于解释/实现?
到目前为止,我发现了两个示例,这些示例说明了x86 asm(one , two)中系统调用的错误处理以类似的方式解决了该问题。首先,他们执行“幻像”操作,例如or eax,eax
,以设置适当的flags
,接着,他们测试符号标志(SF)的条件,以查看符号位是否已设置,以及采取相应行动。
同样,我不明白我们如何从科学基金会确定该数字实际上为负。它仅表示符号位(最高有效位)已被设置。
作为示例,假设我们的代码实现了系统调用,返回值为0x8000 0000 0000 0000h:
mov rax,8000000000000000h ; The illustrative return value from our syscall
test rax,rax ; Perform 'test' to set flags accordingly
jns Exit ; If SF set, 'fall-through' to 'Error'
; Write error message to stdout:
Error:
mov rax,4 ; sys_write
mov rbx,1 ; File descriptor 1, stdout
mov rcx,ErrorMsg ; Pass offset of message
mov rdx,ERRORLEN ; Length of error message
int 80h ; Kernel call
; Exit program:
Exit:
mov rax,1 ; exit system call
mov rbx,0 ; return a code of zero
int 80h ; make kernel call
在这种情况下,我们会(错误地)假设发生了错误,将错误消息写入stdout
并退出程序。我赞赏这种情况不太可能发生。但是,我将其声明为可能的错误是错误的吗?如果是这样,为什么?
或者,简单地讲,答案是,鉴于Linux下的所有系统调用,都没有可能的返回值,该返回值会返回足以将符号位设置为64位数字的值;还是32位数字?
如何在Linux下实现对系统调用的错误处理,从而避免出现上述情况。
在Linux下,x86 asm中错误处理系统调用的标准约定是什么?
............................................... .....
NASM 2.11.08版x86架构| Ubuntu 16.04
答案 0 :(得分:4)
系统调用的合法返回值始终为正(有符号)整数或地址。当它们为正整数时,负值可以用作错误代码,因此任何负值都是错误。
因此,唯一棘手的情况是返回值是地址。事实证明,与-4096 ..- 1范围内的整数对应的地址全部在内核保留的页面中,内核永远不会返回-因此该范围内的任何位模式都只会作为错误返回代码,而不是有效地址。
此外,与x86_64中的负整数相对应的所有地址都为内核保留或无效-用户地址将始终在0..2 47 -1范围内。因此,对于x86_64,您只需要检查%rax的符号位(最高位)-如果将其设置,则出现错误。
test %rax, %rax
js error
不是32位x86代码,情况并非如此-一些有效地址为负数。因此,在这种情况下,您需要显式检查错误范围,这实际上是最简单的无符号比较
cmpl %eax, 0xfffff000 # unsigned 2^32 - 4096, aka signed -4096
ja error # -4095 .. -1 is an error, anything else is non-error