-
Notifications
You must be signed in to change notification settings - Fork 8
Display Windows NTSTATUS exit codes in hex #21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
On Windows, "negative" exit codes are probably NTSTATUS values. For example, if a program accesses an invalid memory location, Unix sends a SIGSEGV signal which, if unhandled, will terminate the process (setting some kind of non-zero exit code - for example, Linux sets the exit code to 128 + signal number to give a fairly memorable 139). In the equivalent scenario, Windows throws an EXCEPTION_ACCESS_VIOLATION which, if handled by the default exception handler, will terminate the process with exit code STATUS_ACCESS_VIOLATION. These codes are large negative numbers, which are not terribly memorable in decimal, so for negative exit codes we instead display them in hexadecimal as 0xc0000005 is slightly more memorable than -1073741819.
626f114
to
a0e4d16
Compare
| `Exited n -> Fmt.pf ppf "@[exited [%d]@]" n | ||
| `Exited n -> | ||
if Sys.win32 && n < 0 then | ||
Fmt.pf ppf "@[exited [0x%08lx]@]" (Int32.of_int n) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you.
I'm rather add an Fmt.exit_code
to b0__fmt
. I'm pretty sure I must be printing exit code in other places. I'm not asking you to do the work because b0_std
is gradually becoming a vendoring of more
which I intend to distribute separately at some point.
But I do have a question here. Why to you convert to int32
rather than simply format with "0x%08x"
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But I do have a question here. Why to you convert to
int32
rather than simply format with"0x%08x"
?
Ouch, I copy-pasted this code from a hacker without thinking about it hard enough. NTSTATUS
is a long
, so a signed, 32-bits integer. The error codes are in the 0xC0000000 - 0xFFFFFFFF
range, so on 64-bits platforms there shouldn't be any problem using an OCaml int
, but on 32-bits, taking into account the OCaml bit tagging, as the exit code will be stored into a 31-bits integer, do we lose 1 bit? my brain just crashed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but on 32-bits, taking into account the OCaml bit tagging, as the exit code will be stored into a 31-bits integer, do we lose 1 bit? my brain just crashed.
Yes on a 32-bit platform you'd lose a bit, in the error code part of the encoding
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess there's nothing we can do for 32-bit platforms anyway, the exit code will always be incorrect. Maybe we could print the last bit as ?
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes perhaps adding a "?"
at the right place apt.
That being said my own head is exploding now. If I look at the table here the codes seem to be in the lsb (which is not what I read in the chart I linked to). So it's not the code that would end up being botched on a 32-bit platform but rather one of the severity bits.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I've given this a lot of thoughts and this is the result I came up with. The exit codes on the C side are 32-bit unsigned integers, so on OCaml 64 bits there's nothing particular to be done. On OCaml 32-bit we loose the MSB.
If we assume that no sane application would set the two most significant bits, and that they usually indicate an NTSTATUS error, we can check for the 2nd most significant bit. If it is set, we may assume that the exit code represented an NTSTATUS, and set the MSB. This gives:
let conv n =
if Sys.win32 then
let n = Int32.of_int n in
if Sys.word_size == 32 then
(* We've lost the MSB converting from C to OCaml. We suppose
that no sane application would set the two most significant
bits, that usually indicate a STATUS_SEVERITY_ERROR. Check
for the second MSB. *)
let msb = Int32.shift_left 1l 31 in
if Int32.(compare (logand (shift_right n 30) 1l) 1l) == 0 then
Printf.sprintf "0x%08lX" (Int32.logor n msb)
else
(* Technically, [Int32.(logor n msb)] is also possible. *)
Int32.(logand n (lognot msb) |> to_string)
else
let status_severity_error = Int32.(shift_left 0x3l 30) in
if Int32.(unsigned_compare (logand n status_severity_error) 0l) != 0 then
(* Usually indicates an NTSTATUS in the error range. *)
Printf.sprintf "0x%08lX" n
else
Int32.to_string n
else
string_of_int n
Interestingly, if an executable exits with 0x80000000U
, 32-bit OCaml code comparing the result with 0
will consider that the process exited successfully.
I've already pushed (broken?) patches to opam and dune with the code I've first submitted. If you adopt this new idea or manage to shorten it, I'll update my previous submissions with the new code, with your permission.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some code to help testing here: https://gist.github.com/MisterDA/48051cf0953d873fc02188e9635916b0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MisterDA sorry to have lost the ball on this. Are you positive that conv
is what we want ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm still not totally confident that the 32-bits code is correct. It seems to behave correctly but I'm not sure I fully grasp what's happening to OCaml integers and shifts and signed/unsigned conversion…
In fact that comes out of a |
On Windows, "negative" exit codes are probably
NTSTATUS
values. For example, if a program accesses an invalid memory location, Unix sends aSIGSEGV
signal which, ifunhandled
, will terminate the process (setting some kind of non-zero exit code - for example, Linux sets the exit code to 128 + signal number to give a fairly memorable 139). In the equivalent scenario, Windows throws anEXCEPTION_ACCESS_VIOLATION
which, if handled by the default exception handler, will terminate the process with exit codeSTATUS_ACCESS_VIOLATION
. These codes are large negative numbers, which are not terribly memorable in decimal, so for negative exit codes we instead display them in hexadecimal as0xc0000005
is slightly more memorable than-1073741819
.I saw this error code while running
odig log -e
, but maybe it doesn't originate from a b0 call.I'm pushing for this change throughout the stack (already patched Dune, opam, and ocaml-ci).