问题描述
我找不到在Linux下如何正确实现x86程序集中的系统调用错误处理的明确答案.
显示从系统调用返回的寄存器rax
包含系统调用的结果.值在-4095到-1之间表示错误.
从逻辑上讲,如果我们查看" rax
中的返回值,并且该值在此范围内,则可以得出结论:发生了错误,并且可以对该信息采取适当措施.
但是,我们如何知道返回值确实是负值?这实际上是我提出问题的基础,因为我的理解是,如果我们这样对待给定的二进制模式,那么它的值只会是负值.
例如,为便于说明,我们假设系统调用的返回值为"-4000".现在,传递给rax
的实际返回值实际上不是-4000,而是可以这样解释的二进制模式.在解释系统调用的返回值的情况下,如何区分这种二进制模式的一种可能解释和另一种解释?换句话说,我们如何知道返回值(二进制模式)不代表无符号等效项?
诚然,这将是一个相对较大的数字.但是,这不是一个合理的情况吗?毕竟,rax
仅包含位,我们是否将这些位视为代表负数取决于解释/实现?
到目前为止,我发现了两个示例,这些示例说明了x86 asm中系统调用的错误处理( )以类似的方式解决了该问题.首先,他们执行幻像"操作(例如or eax,eax
)来设置适当的flags
,然后他们测试符号标志(SF)的条件,看是否设置了符号位,并采取相应的行动. /p>
同样,我不明白我们如何从科学基金会确定该数字实际上是负数.它仅表示符号位(最高有效位)已被设置.
作为示例,假设我们的代码实现了系统调用,返回值为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
系统调用的合法返回值始终为正(有符号)整数或地址.当它们为正整数时,负值可以用作错误代码,因此任何负值都是错误.
因此,唯一棘手的情况是返回值是地址.事实证明,与-4096 ..- 1范围内的整数对应的地址全部在内核保留的页面中,内核永远不会返回-因此该范围内的任何位模式都只会作为错误返回代码,而不是有效地址.
此外,与x86_64中的负整数相对应的所有地址都为内核保留或无效-用户地址将始终在0..2 -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
I cannot find a definitive answer for how to correctly implement error handling for system calls in x86 assembly, under Linux.
Shows that, returning from a syscall, register rax
contains the result of the system-call. A value in the range between -4095 and -1 indicates an error.
Logically then, if we ‘look’ at the return value in rax
, and the value lies within this range, we can conclude that an error has occurred, and we can act on this information appropriately.
However, how do we know that the return value is indeed a negative value? This is the basis for my question really, as my understanding is such that, the value of a given binary pattern is only a negative value if we treat it as such.
For example, let’s assume, for purposes of illustration, that the return value from a system call is ‘-4000’. Now, the actual return value passed to rax
is not literally -4000, but a binary pattern that can be interpreted as such. In the case of interpreting a return value from a system call, how do we differentiate between one possible interpretation of this binary pattern and the other; in other words, how do we know that the return value (binary pattern) does not represent the unsigned equivalent?
Admittedly this would be a relatively large number. However, is this not a legitimately plausible scenario? After all, rax
contains only bits, whether or not we treat those bits as representing a negative number is down to interpretation/implementation?
The two examples I have found so far, that illustrate error handling for system calls in x86 asm (one , two) approach the problem in a similar manner. First they execute a ‘phantom’ operation, such as or eax,eax
, to set the appropriate flags
, next they test the condition of the sign flag (SF), to see if the sign bit is set, and act accordingly.
Again, I do not understand how we can determine from the SF, that the number is in fact negative; it is simply indicating that the sign bit (most significant bit) has been set.
As an example, let’s assume our code implemented a system call, with a return value of 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
In this scenario, we would have (wrongly) assumed an error had occurred, written an error message to stdout
and exited the program. I appreciate this is a somewhat unlikely scenario. However, am I wrong in stating this as a possible bug? If so, why?
Alternatively, is the answer, simply, that there are no possible return values, given all of the system calls under Linux, that would ever return a value large enough to set the sign bit in a 64-bit number; or a 32-bit number for that matter?
How to implement error handling for system calls, under Linux, in such a way that a scenario such as laid about above, would be avoided.
What is the standard convention for error handling system calls in x86 asm, under Linux?
....................................................
NASM version 2.11.08 Architecture x86 | Ubuntu 16.04
The legitimate return values from system calls are always either positive (signed) integers or addresses. When they are positive integers, the negative values can be used as error codes, so any negative value is an error.
So the only tricky case is when the return value is an address. It turns out that the addresses corresponding to integers in the range -4096..-1 are all in a kernel reserved page that will never be returned by the kernel -- so any bit pattern in that range will only ever be returned as an error code, and not as a valid address.
In addition, ALL addresses that correspond to negative integers in x86_64 are reserved for the kernel or invalid -- user addresses will always be in the range 0..2-1. So for x86_64 you need only check the sign bit (top bit) of %rax -- if it is set, there was an error.
test %rax, %rax
js error
Fo 32-bit x86 code, this is not the case -- some valid addresses are negative numbbers. So in that case, you need to explicitly check for the error range, which is actually easiest to do with an unsigned comparison
cmpl %eax, 0xfffff000 # unsigned 2^32 - 4096, aka signed -4096
ja error # -4095 .. -1 is an error, anything else is non-error
这篇关于Linux下x86汇编中系统调用的错误处理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!