So over the past few days, with all of the CM4DX hype, a lot of people have been asking what this 2nd-init thing is that solved all of the problems in DX land and made the world suddenly fill with sunshine and win. Well, I've decided to do a little write-up on that very subject.
2nd-init has actually been around for awhile. It was originally developed (and I may be wrong, this is all of the information I was able to retrieve) by the group of Motorola Milestone developers known as And Developers. Its latest incarnation was the result of a very talented developer known as Skrilax_CZ. But why do we need 2nd-init?
When your phone boots up, the first thing that runs is the kernel. It initializes all of your hardware and puts the system into a state where you can actually run binaries (programs) on top of it. Once this low-level initialization is complete though, the kernel needs a way to tell the system to start booting its programs. This is where init comes in. By default, the Linux kernel will look for a program located at /init and run that with process id 1. It is then understood that this program is to never exit, and manage starting and stopping all of the various services that need to be run on the system.
This is fine and dandy, except that for Android, init lies on the ramdisk of the boot partition. As we all know, on devices such as the Motorola Droid X, the Motorola Milestone, the Motorola Defy, et. al., the boot partitions cannot be modified. Thus we are stuck with Motorola's implementation of init, as well is its various RC scripts that define what services to start up (such as MotoBlur).
In the past I had attempted to jail the Android system into a new root and spawn another init process with a clean slate, but occasionally a system called the kernel watchdog would wake up Motorola's init and cause battery drains that were too substantial to make the device usable.
It was also impossible to “kill and restart” init, because as I mentioned init is not supposed to exit. If it does, the kernel panics because it thinks that something horrible has happened in the system, and the entire device restarts.
So where do we go from here? As we've mentioned that we can't kill init, we can't modify the ramdisk to have a new init, we can't use Motorola's init, and we can't jail ourselves and hope for the best. So what do we do?
First let's get everything set up so 2nd-init can do its thing. This is where hijack comes in.
When Motorola's init is running, at the point where it tries to mount /data, we actually hijack the init through a custom program (aptly called “hijack”). This program first mounts /data, and checks for a file, “/data/.recovery_mode”. If that file exists it boots into CWR. If not then we continue on under the assumption that we are in “boot mode”.
Once hijack goes into “boot mode”, it does a couple of things. First, it remounts the root partition (/) to be read-write. It then copies the contents of the CyanogenMod ramdisk on top of the Motorola ramdisk contents (this include the /init binary and the various init RC scripts).
After overwriting the ramdisk contents, hijack then starts killing everything and everyone in sight. It's literally mass chaos on your system. Services are being shut down. Partitions are being unmounted. Stray processes are being killed. Essentially it leaves no prisoners until all that's left of Motorola's system is the Motorola init process. Process ID 1. And there can only be one. So then hijack pulls out its trump card, and fires off 2nd-init.
The first thing 2nd-init does is load up a library called ptrace. What ptrace does, is it allows a process to essentially attach to another, and control it. This is where 2nd-init does its magic.
Because init is always running with a process ID of 1, it's easy for 2nd-init to find. It basically tells ptrace “I want to attach to PID 1”, and then waits. As soon as init gets thrown a signal (which happens all the time), init is put to sleep, and 2nd-init is given control of init. At this point we're in.
Now to get an idea of how 2nd-init works its magic we need to delve into a little known fact about how init works. As we have mentioned before, init spawns processes. It does this by calling a system call known as execve. This system call essentially tells the kernel “Hey, I want you to take this file (which is an executable), and load it into memory on top of me with a given environment, and then run it.” When execve is called, it never returns, because the calling process is overwritten by the new one. Because of this, typically you spawn a new process before calling it, which is what init usually does, but 2nd-init is in control now.
2nd-init starts by scanning the various instructions that make up init, until it finds that magic location where init makes a call to execve. At this point, it then injects code into init's process, that force the parameters to execve to load the binary located at /init (which we have overwritten in the ramdisk to be our own), and then tell it to use the exact same environment that the currently running init is using.
The last step that 2nd-init does, is alter init's PC. The PC essentially tells a process what instruction within its code it is currently running, and where to go next. 2nd-init alters this to point to the location of the execve call that it just injected parameters into.
At this point 2nd-init detaches and allows init to begin running again. Then we watch the magic happen.
Once init regains control of itself, it looks at its PC and executes execve. Because the PC was pointing directly at execve, we bypass the spawning of a new process, and init calls it on itself. The kernel then looks at the parameters for execve which now say “load /init using the current environment”, and the kernel overwrites Motorola's init with our own, and runs it from scratch.
At this point Motorola's init no longer exists, and the CyanogenMod init can run wild and free in the breeze, doing what it needs to do to produce a fast, clean, and robust system from the ground up. Partitions are remounted, services are restarted, and all of it without MotoBlur.
First off, I want to make it clear that I myself did not write 2nd-init. That work of genius was written by others, its current incarnation of which was done by Skrilax_CZ. I simply rebuilt it from its source to run on the Motorola Droid X, and analysed it for my own amusement.
It's also important to remember that 2nd-init does NOT unlock the bootloader, nor does it allow you to run a custom kernel. It simply allows us to utilize the kernel that is there for our own means.
With that I want to thank the entire Milestone and And-Developers groups for all of the work they have put into working with locked-down Motorola devices. They have been dealing with this crap for a lot longer than we have, and without a lot of the headway they had done before us this would have been much more difficult to implement.
2nd-init uses PFM to reload the initialization process.
2nd-init does NOT unlock the bootloader or let you run a custom kernel, but that's fine.