ONLamp.com
oreilly.comSafari Books Online.Conferences.

advertisement


Linux Compatibility on BSD for the PPC Platform: Part 2
Pages: 1, 2, 3, 4

Dumping the stack, you can see the parameters you give to the program and its environment. Stackdump also give you the address of argc, which 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 entering 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.

argc=0x7fffe8a8
argv=0x7fffe8ac

<snip> 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 **argv, at 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 argc value 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 argc we have **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 identify.

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 argc on a 16-byte boundary. And there was a special trick if argc was 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 argc, **argv and **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 COMPAT_LINUX's struct execsw. This is done in sys/kern/exec_conf.c, using the linux_copyargs_function macro. That macro should be defined in sys/compat/linux/arch/powerpc/linux_exec.h.

Thus, by modifying this macro, we can use a customized copyargs() function. The Alpha port of COMPAT_LINUX already did this. The customized function is linux_copyargs(), and it is in the sys/compat/linux/arch/alpha/linux_exec_alpha.c file. Because there is already a linux_exec.c in 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 argv and 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 **argv and **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

The LINUX_SHIFT command is a macro, defined as 0x0000000fUL in 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 architecture-independent.

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 argc, **argv, and **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.

Pages: 1, 2, 3, 4

Next Pagearrow





Sponsored by: