If a program's main returns an i32, why is $? (as measured by the shell that called it) truncated to 8 bits?

Viewed 46

Sorry for such a noob question, but why the result is not 516?

define i32 @main()
{
        %1 = add i32 6, 500
        %2 = add i32 5, 5
        %3 = add i32 %1, %2
        ret i32 %3
}

http://llvm.org/docs/LangRef.html#integer-type

i32 a 32-bit integer.

Usage:

./lli Program.ir; echo $?
4

Thanks in advance

2 Answers

The exit code of a process in Unix is only 8 bits. Any larger value gets truncated, regardless of whether LLVM is involved:

$ ( exit 516 ); echo $?
4

The exit code (I'm going to distinguish the exit value returned by your program, from the exit code made available to the process that started you program) is actually, in UNIX like operating systems, a conglomeration of several different items, one of which is the exit value. See, for example, this link, which contains (with my emphasis and [extra information]):

Don't confuse a program's exit status [value] with a process' termination status [code]. There are lots of ways a process can terminate besides having its program finish. In the event that the process termination is caused by program termination (i.e., exit), though, the program’s exit status [value] becomes part of the process' termination status [code].

The macro to get the actual exit status from the process (see here) states:

If WIFEXITED is true of status, this macro returns the low-order 8 bits of the exit status value from the child process.

That's also indicated by the actual source code of the Linux exit_group syscall, which is the one eventually called by exit:

SYSCALL_DEFINE1(exit_group, int, error_code)
{
    do_group_exit((error_code & 0xff) << 8);
    /* NOTREACHED */
    return 0;
}

You can see there that it only uses the lower eight bits of the exit value, and shifts it left so it can store those other items (control information) in there, all zero in this case. Contrast that with the same call from the signal processor which only sets the control information:

do_group_exit(ksig->info.si_signo)

In other words, it also has to put other things in the process exit code, such as which signal terminated it (if it was terminated by a signal), whether it dumped core, and so on. That's why the exit value is limited to a lesser range than you expect.

The ISO standard (C11) also allows for this, in 7.22.4.4 The exit function /5 (since returning an integer value from main() is equivalent to calling exit() with that value:

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.

Related