Dumping the stack, you can see the parameters you give to the program
and its environment.
Stackdump also give you the address of
is the place where the program stores
argc on the stack. In fact, the
program copied that value from an upper address on the stack before
main(). If we do not get the appropriate value for
argc, we must find out where the program gets its
argc, and fix the way
the NetBSD kernel sets up the stack so that
argc gets written where the
emulated binary expects it.
Note: This is a stack dump with the desired stack layout, not the original one.
7fffe8a0 7fff e8c0 0180 0744 0000 0001 7fff e904 ................
7fffe8b0 7fff e8b0 0000 0006 0184 0000 0184 0000 ................
7fffe8c0 7fff e8e0 0180 05cc 0000 0000 0000 0000 ................
7fffe8d0 7fff e8e0 4186 65e0 7fff e9e0 4186 5d60 ....A.e.....A.]'
7fffe8e0 7fff e8f0 4188 9580 7fff e9e0 4186 5d60 ....A.......A.]'
7fffe8f0 0000 0000 0000 0000 0000 0000 0000 0000 ................
7fffe900 0000 0001 7fff eab0 0000 0000 7fff eab5 ................
Next to this copied
argc, here at
0x7fffe8a8, stands a pointer to
0x7fffe8ac. This is more interesting because looking at the
pointed address, at
0x7fffe904, we can find the
**argv pointer that was
set up by the kernel. Next to it, at
0x7fffe900, we have the
set up by the kernel. In this example, everything is fine, but if the
kernel does not set up
argc at the place the executable expects it,
searching around the place pointed by the pointer to
**argv (here at
0x7fffe8ac) is a good option.
When searching for the
argc value set up by the kernel, the idea is to
look for an integer value (4 bytes on the PowerPC) equal to the actual
number of arguments given to the program (the program name itself being
the first argument, so that number is at least 1). Next to
**argv, which points to the
*argv array. Each element of this array
is a pointer to a null terminated argument string, so it is easy to
We can figure out what the problem is by trying
stackdump with various
arguments. On the PowerPC, the problem was that we needed to set up
on a 16-byte boundary. And there was a special trick if
already to appear on a 16-byte boundary, because the emulated binary
then expected it to be 16 bytes lower on the stack.
To fix this problem, and get arguments passed to the program, we
need to modify the stack pointer before writing
**envp on the stack. Setting up the stack is normally done by the
copyargs() function, which lives in
sys/kern/kern_exec.c. But it is
possible to supply a customized
copyargs() function by filling the
appropriate field of
struct execsw. This is done in
sys/kern/exec_conf.c, using the
linux_copyargs_function macro. That
macro should be defined in
Thus, by modifying this macro, we can use a customized
function. The Alpha port of
COMPAT_LINUX already did this. The
customized function is
linux_copyargs(), and it is in the
Because there is already a
sys/compat/linux/common, this file
cannot be called
linux_exec.c, because when you build the kernel, all
object files fit in the same build directory. Having the same name twice
will result in the second object file overwriting the first one, and
this will lead to a link error. That file was intended to be architecture-independent, so we use the Alpha version
with some PowerPC add-ons. The result is the
sys/compat/linux/arch/powerpc/linux_exec_powerpc.c file, which is common
to the Alpha and the PowerPC platforms. It should be moved to the architecure-independent
sys/compat/linux/common/linux_exec.c file later.
Linux_copyargs() first calls the standard
copyargs() function, to set up all the
envp arrays. It leaves a
linux_elf_aux_argsize bytes gap for the ELF auxiliary table (we will take a look at this later), and then it attempts to write
argc, and the
**envp pointers. The PowerPC-specific alignment is done by this code section:
#ifdef LINUX_SHIFT /* * Seems that PowerPC Linux binaries expect * argc to start on a 16 bytes * aligned address. And we need one more 16 * byte shift if it was already * 16 bytes aligned. */ (unsigned long)stack = ((unsigned long)stack - 1) & ~LINUX_SHIFT; #endif
LINUX_SHIFT command is a macro, defined as
sys/compat/linux/arch/powerpc/linux_exec.h, and we use an
ifdef test to prevent the Alpha version to do this PowerPC-specific fix that
would break NetBSD/Alpha Linux emulation. The file remains
With this fix, we managed to get statically linked executables to get their
arguments. However, a dynamically linked program will still fail because
ld.so does not find the ELF auxiliary table.
The ELF auxiliary table
The ELF dynamic linker (
ld.so) needs more information than just
**envp to actually link a program. It must be able to locate the ELF section where the list of shared libraries needed by the program is located. This kind of information is transmitted to
ld.so by setting up the ELF auxiliary table on the stack. This table contains a few
entries, each containing two fields: type and value. The details of
each field are specified in the System V Release 4 PowerPC ABI, that
can be found here.