Skip to content
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

::std::process::exit does not document interpretation of the argument #35046

Closed
nagisa opened this issue Jul 26, 2016 · 10 comments
Closed

::std::process::exit does not document interpretation of the argument #35046

nagisa opened this issue Jul 26, 2016 · 10 comments

Comments

@nagisa
Copy link
Member

nagisa commented Jul 26, 2016

On systems where the glibc exit call is used, the exit code returned to the OS is actually code & 0xff. This made my exit code of 0xb00_b00 be a success.

We should either figure out how to make glibc pass through the exit code verbatim, or document this atrocity.

@abonander
Copy link
Contributor

I don't know how to ask this more politely so I'll just go for it. Did you mean for your error code to spell out "boob" or is that a coincidence and the error code has some other semantic meaning to your program?

@nagisa
Copy link
Member Author

nagisa commented Jul 26, 2016

I was meaning to spell out “Boo! Boo!”, because I remembered it accepting a i32 and had nothing better to do this evening. Alas.

EDIT: I adjusted the report and inserted a _ in appropriate place.

@tbu-
Copy link
Contributor

tbu- commented Jul 26, 2016

It seems to me that Rust is already doing the correct thing:

use std::process;

fn main() {
    process::exit(0xf00ba1);
}

Strace:

execve("./a", ["./a"], [/* 33 vars */]) = 0
brk(NULL)                               = 0x55618643c000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=314472, ...}) = 0
mmap(NULL, 314472, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f4f9f7a5000
close(3)                                = 0
open("/usr/lib/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\240\r\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=14608, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4f9f7a4000
mmap(NULL, 2109680, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f4f9f3cb000
mprotect(0x7f4f9f3cd000, 2097152, PROT_NONE) = 0
mmap(0x7f4f9f5cd000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7f4f9f5cd000
close(3)                                = 0
open("/usr/lib/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0a\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=143584, ...}) = 0
mmap(NULL, 2212880, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f4f9f1ae000
mprotect(0x7f4f9f1c6000, 2093056, PROT_NONE) = 0
mmap(0x7f4f9f3c5000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x17000) = 0x7f4f9f3c5000
mmap(0x7f4f9f3c7000, 13328, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f4f9f3c7000
close(3)                                = 0
open("/usr/lib/libgcc_s.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0p*\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=725296, ...}) = 0
mmap(NULL, 2185552, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f4f9ef98000
mprotect(0x7f4f9efae000, 2093056, PROT_NONE) = 0
mmap(0x7f4f9f1ad000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x15000) = 0x7f4f9f1ad000
close(3)                                = 0
open("/usr/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`\10\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1960968, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4f9f7a3000
mmap(NULL, 3803440, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f4f9ebf7000
mprotect(0x7f4f9ed8e000, 2097152, PROT_NONE) = 0
mmap(0x7f4f9ef8e000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x197000) = 0x7f4f9ef8e000
mmap(0x7f4f9ef94000, 14640, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f4f9ef94000
close(3)                                = 0
open("/usr/lib/libm.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0pU\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1063296, ...}) = 0
mmap(NULL, 3158248, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f4f9e8f3000
mprotect(0x7f4f9e9f6000, 2093056, PROT_NONE) = 0
mmap(0x7f4f9ebf5000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x102000) = 0x7f4f9ebf5000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4f9f7a2000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4f9f7a0000
arch_prctl(ARCH_SET_FS, 0x7f4f9f7a07c0) = 0
mprotect(0x7f4f9ef8e000, 16384, PROT_READ) = 0
mprotect(0x7f4f9ebf5000, 4096, PROT_READ) = 0
mprotect(0x7f4f9f3c5000, 4096, PROT_READ) = 0
mprotect(0x7f4f9f5cd000, 4096, PROT_READ) = 0
mprotect(0x7f4f9f7f2000, 4096, PROT_READ) = 0
munmap(0x7f4f9f7a5000, 314472)          = 0
set_tid_address(0x7f4f9f7a0a90)         = 18917
set_robust_list(0x7f4f9f7a0aa0, 24)     = 0
rt_sigaction(SIGRTMIN, {0x7f4f9f1b3ba0, [], SA_RESTORER|SA_SIGINFO, 0x7f4f9f1bef00}, NULL, 8) = 0
rt_sigaction(SIGRT_1, {0x7f4f9f1b3c30, [], SA_RESTORER|SA_RESTART|SA_SIGINFO, 0x7f4f9f1bef00}, NULL, 8) = 0
rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
getrlimit(RLIMIT_STACK, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
readlink("/etc/malloc.conf", 0x7ffda7c2d4c0, 4096) = -1 ENOENT (No such file or directory)
brk(NULL)                               = 0x55618643c000
mmap(NULL, 2097152, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4f9e6f3000
munmap(0x7f4f9e6f3000, 2097152)         = 0
mmap(NULL, 4190208, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4f9e4f4000
munmap(0x7f4f9e4f4000, 1097728)         = 0
munmap(0x7f4f9e800000, 995328)          = 0
open("/sys/devices/system/cpu/online", O_RDONLY|O_CLOEXEC) = 3
read(3, "0-1\n", 8192)                  = 4
close(3)                                = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f4f9ec2a310}, {SIG_DFL, [], 0}, 8) = 0
mmap(NULL, 2097152, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4f9e400000
open("/proc/self/maps", O_RDONLY|O_CLOEXEC) = 3
getrlimit(RLIMIT_STACK, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
read(3, "55618585e000-5561858b1000 r-xp 0"..., 1024) = 1024
read(3, "  /usr/lib/libc-2.23.so\n7f4f9ef9"..., 1024) = 1024
read(3, "f5ce000 r--p 00002000 fe:00 9210"..., 1024) = 876
close(3)                                = 0
sched_getaffinity(18917, 32, [0 1])     = 16
mmap(0x7ffda742f000, 4096, PROT_NONE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7ffda742f000
rt_sigaction(SIGSEGV, {0x556185868600, [], SA_RESTORER|SA_STACK|SA_SIGINFO, 0x7f4f9f1bef00}, NULL, 8) = 0
rt_sigaction(SIGBUS, {0x556185868600, [], SA_RESTORER|SA_STACK|SA_SIGINFO, 0x7f4f9f1bef00}, NULL, 8) = 0
sigaltstack(NULL, {ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=0}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4f9f7f0000
sigaltstack({ss_sp=0x7f4f9f7f0000, ss_flags=0, ss_size=8192}, NULL) = 0
sigaltstack({ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=8192}, NULL) = 0
munmap(0x7f4f9f7f0000, 8192)            = 0
exit_group(15731617)                    = ?
+++ exited with 161 +++

@Aatch
Copy link
Contributor

Aatch commented Jul 27, 2016

@tbu- unfortunately, somewhere along the way, it gets truncated down to a single byte. If you run that program and inspect the exit code, you should see 161.

Looks like this is something that is platform-specific and we should just make a note of it in the docs.

@retep998
Copy link
Member

Just did a quick test to verify the behavior on other platforms. On Windows the exit code is definitely a 32bit integer and no truncation of its bits occurs. Whether it is signed or unsigned seems to vary.

@strega-nil
Copy link
Contributor

strega-nil commented Jul 27, 2016

_Noreturn void exit(int status);

Finally, control is returned to the host environment. If the value of status is zero or EXIT_SUCCESS, an implementation-defined form of the status successful termination is returned. If the value of status is EXIT_FAILURE, an implementation-defined form of the status unsuccessful termination is returned. Otherwise the status returned is implementation-defined.

This is the C standard definition of exit, for comparison.

@strega-nil
Copy link
Contributor

We could do something like "if status is zero, then successful; if status is one, then unsuccessful; otherwise, implementation-defined"

@retep998
Copy link
Member

retep998 commented Jul 27, 2016

According to http://man7.org/linux/man-pages/man2/waitpid.2.html

       WEXITSTATUS(wstatus)
              returns the exit status of the child.  This consists of the
              least significant 8 bits of the wstatus argument that the
              child specified in a call to exit(3) or _exit(2) or as the
              argument for a return statement in main().  This macro should
              be employed only if WIFEXITED returned true.

@nagisa
Copy link
Member Author

nagisa commented Jul 27, 2016

The exit() function causes normal process termination and the value of status & 0377 is returned to the parent (see wait(2)).

The C standard specifies two constants, EXIT_SUCCESS and EXIT_FAILURE, that may be passed to exit() to indicate successful or unsuccessful termination, respectively.

The use of EXIT_SUCCESS and EXIT_FAILURE is slightly more portable (to non-UNIX environments) than the use of 0 and some nonzero value like 1 or -1. In particular, VMS uses a different convention.

from linux’s exit(3)

Note that typically _exit(2) only passes the lower 8 bits of status on to the parent, thus negative values have less meaning.

from OpenBSD exit(3)

The value of status may be 0, EXIT_SUCCESS, EXIT_FAILURE, or any other value, though only the least significant 8 bits (that is, status & 0377) shall be available to a waiting parent process.

from POSIX exit(3)

Both functions make the low-order eight bits of the status argument available to a parent process which has called a wait(2)-family function.

from OS X exit(3)


Common topic here (as also pointed out by other people, already) is that its not the exit code that gets passed to exit(2)/exit_group(2) which is truncated, but the exit code that is inspected by wait.

IMO in this case both process::exit and various ways to wait for children in libstd should mention this in some way.

@frewsxcv
Copy link
Member

Opened a PR: #38397

If you have a moment, please give feedback! 😺

frewsxcv added a commit to frewsxcv/rust that referenced this issue Dec 16, 2016
GuillaumeGomez added a commit to GuillaumeGomez/rust that referenced this issue Dec 18, 2016
…exit, r=alexcrichton

Document platform-specific differences for `std::process::exit`.

Fixes rust-lang#35046.
frewsxcv added a commit to frewsxcv/rust that referenced this issue Dec 18, 2016
sanxiyn added a commit to sanxiyn/rust that referenced this issue Dec 19, 2016
…exit, r=alexcrichton

Document platform-specific differences for `std::process::exit`.

Fixes rust-lang#35046.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants