update: It finally works!
So after a day of much fun and hacking, I sadly blog here in the face of defeat. Thwarted by binary only distribution and non-forwards compatible architectures. This tale of woe documents my attempt to get the Android stack running on the FIC Neo 1973 phone.
This post describes what I did to try and get this working, and ultimately why it isn't going to work until you get the source for the stack. And if you find the story excruciatingly just skip to the conclusion.
The day started out promisingly. I took the diff of the kernel I had earlier produced, and started to hack it down into something a little more manageable. First I got rid of the patches that enabled Qemu, since I only care about running this on the real hardware. Then I got rid of the patches that enabled the goldfish platform. The goldfish platform is the hardware platform that the Android SDK simulates. I don't need that for running on the Neo, so gone! Next there was a whole big patchset for enabling yaffs2. The Openmoko kernel already has yaffs2 patched in there, so this just causes confusion. Once all that is done, the final patch is much more manageable; 8000 lines rather than 30000 lines.
So with that in place, I pulled down the 2.6.22.5 kernel.org kernel, and then applied the Openmoko patchset with quilt. With that in place I applied my stripped down diff. Now this was against 2.6.23, rather than 2.6.22 so there was a bit of fuzz and a couple of failed hunks. (That sounds more, like gangster slang than hacking!). Anyway, after fixing up the patch, I now have patch that applies cleanly against the Openmoko kernel. (Not that it will do you much good as you are about to see.)
So I took the recommended default config for openmoko, and ran trusty
make oldconfig. This prompted for a couple of new options:
PANIC_TIMEOUT=0 CONFIG_BINDER=y CONFIG_LOW_MEMORY_KILLER=y
I ended up removing the low memory killer option because it had compile errors. I was going to go back and fix, but in the end didn't really matter.
This was a lot of progress in my first hour or two of hacking. I then proceeded to waste a whole bunch of time on stupid stuff. I'll save you all the gory details, but highlights follow.
Firstly just trying to get stuff running on Neo 1973 proved a bit of a challenge. I eventually found some known binaries, and workd ut how to get them onto the phone:
$ sudo dfu-util -a 5 -R -D om.rootfs.jffs2 $ sudo dfu-util -a 3 -R -D om.uImage.bin
(Special thanks to David who pointed me at the right places.)
The next challenge was actually just getting the kernel I had built
from to work with the rootfs rather than just the binary kernel. This
was particularly difficult to actually debug because there were no
error messages or panics, just the kernel sitting at Freeing
init memory, and no more. As far as I could tell it was the
same kernel source, and I had used the default config. So after lots
of messing around (different compilers, with/without modules, etc), I
stumbled upon the fact that the user mode binary applications on the
rootfs image are all using the new EABI, as opposed to the old ABI. It
turns out that special kernel support needs to be enabled for EABI,
and this isn't in the default openmoko kernel config. (I
don't have a good reference for EABI vs. OABI. Linux devices has a story,
but the floating point stuff is really only one small part of the
differences.). Anyway, after enabling the CONFIG_AEBI
things started going a lot smoother. I really wish that more people
enabled the /proc/config.gz option, it would have made
life a lot easier.
So at this point I had a kernel with the Android patches loading
and running the standard OpenMoko distribution. Next step was to run a
different rootfs. Taking the filesytems
I had extracted earlier as well as the rootfs I had extracted (see
this post
for details), I combined these and used mkfs.jffs2 to build
an android jffs filesystem. (For reference the full command is:
$ sudo mkfs.jffs2 -x lzo -r android-root-image -o android.jffs2 --eraseblock=0x4000 --pad -n -squash).
At this point I thought I was home free. How very wrong I was.
On booting this I was back at the dreaded Freeing init
memory, with no other output. Confused with this, I compiled
a very simple hello world program to see if this would work
as a replacement init (just for testing). This didn't work either.
With this failure I tried another tack. I would revert to my known good, of the working openmoko rootfs, and install my hello program on this rootfs just to test it. I didn't think I would have a problem here. It turns out it failed to run. Luckily the openmoko rootfs has gdb, which is great for fixing problems like this. Firing up gdb soon let me to the real problem.
So, it turns out that my hello binary (and all the android
binaries) are compiled for an ARM926Ej-S chip. This is a problem
because the neo1973 has an ARM920T core. Now you would think that
ARM926 and ARM920 would be pretty close. But if you thought that you
would, unfortunately, be wrong, wrong, wrong! The ARM926EJ-S implement
the ARMv5TEJ instruction set, but the ARM920T implements the ARMv4T
instruction set. So what happens in my hello program is that we hit an
ARMv5 instruction, which is undefined in the earlier ARMv5 ISA, which
generates an undefined instruction trap to the kernel, and the kernel
responds by sending SIGILL to the running
process. Assuming that the program hasn't installed any special signal
handlers this will kill the process. And this is what was happening to
my hello program, and what I assumed was happening to
init as well. (Of course, assumptions make an ass out of
u and me, or in this case, mostly me.)
Now I really wasn't going to be daunted by a pesky little thing such as the CPU not implementing the instructions stand in my way! (Note: I could of course have compiled hello for ARMv4 architecture, but that isn't an option for the rest of the stack, and I was only interested in getting hello running so I could get the rest of the stack running). So, in an act of stupid defiance, I decided, if the CPU can't implement the instruction, I'll do it myself.
Luckily the kernel provides a neat infrastructure for managing
undefined instructions, and even emulating them. So the first instruction
to emulate was the ARM clz instruction. This is the instruction
that counts the number of leading zero bits. The code below implements this.
The only other thing to do is ensure that this hook is
registered at startup using: register_undef_hook(&clz_hook);
static int clz_trap(struct pt_regs *regs, unsigned int instr)
{
/* Extract the source register index */
int src = instr & 0xf;
/* Extract the destination (result) register index */
int dst = (instr >> 12) & 0xf;
/* Extract the conditional code */
int cc = (instr >> 28) & 0xf;
/* Test if the conditional code passes */
if (handle_cc(regs, cc)) {
/* Implement the instruction */
regs->uregs[dst] = 32 - fls(regs->uregs[src]);
}
/* Print some stuff for debugging */
printk("emulating clz: %x src=%d (%lx) dst=%d (%lx) @ %p\n", instr,
src, regs->uregs[src], dst, regs->uregs[dst], (void*) regs->ARM_pc);
/* Increment the PC register */
regs->ARM_pc += 4;
return 0;
}
static struct undef_hook clz_hook = {
.instr_mask = 0x0fff0ff0,
.instr_val = 0x016f0f10,
.cpsr_mask = PSR_T_BIT,
.cpsr_val = 0,
.fn = clz_trap,
};
One thing that may not be clear from the comments is that ARM
supports conditionally executed instructions. The top 4 bits
of the instruction are its condition field. Depending on the condition
field, and the value of the N, Z,
C and V flags (which are stored in
the CPSR register), the instruction may or may not be executed. This
is used to avoid having to branch for all if statements and
the associated problems... but you didn't come here for an introduction
to computer architecture. To correctly implement this, some code is needed,
and I clag it here for posterity.
/* Return true if conditional code should be executed */
static int handle_cc(struct pt_regs *regs, int cc)
{
int doit = 0;
int cpsr = regs->ARM_cpsr;
int n = (cpsr >> 31) & 1;
int z = (cpsr >> 30) & 1;
int c = (cpsr >> 29) & 1;
int v = (cpsr >> 28) & 1;
switch (cc) {
case 0:
doit = z;
break;
case 1:
doit = !z;
break;
case 2:
doit = c;
break;
case 3:
doit = !c;
break;
case 4:
doit = n;
break;
case 5:
doit = !n;
break;
case 6:
doit = v;
break;
case 7:
doit = !v;
break;
case 8:
doit = c && !z;
break;
case 9:
doit = !c || z;
break;
case 10:
doit = (n == v);
break;
case 11:
doit = (n != v);
break;
case 12:
doit = ((z == 0) && (n == v));
break;
case 13:
doit = ((z == 1) || (n != v));
break;
case 14:
doit = 1;
break;
case 15:
doit = 0;
break;
default:
printk("Error, should get here!\n");
}
return doit;
}
OK, one down. That wasn't so hard. The next one gets a little bit
tricker. The compiler will use the BLX instruction if it
is available. This is the Branch, Link and Exchange instruction.
There are two versions of the instruction, and at this stage we only really
care about version 2. In this version the address to branch to is stored in
a register, and a flag indicates whether or not an exchange
is required. (You can ignore exchange for now, more about that later.).
This instruction is a little bit more effort to implement, but it is not too hard:
static int blxv2_trap(struct pt_regs *regs, unsigned int instr)
{
int rm = instr & 0xf;
int cc = (instr >> 28) & 0xf;
printk("emulate blxv2: %x rm=%d (%lx) @ %p CC: %d cc(%lx) cpsr(%lx)\n", instr, rm,
regs->uregs[rm], (void*) regs->ARM_pc, handle_cc(regs, cc), cc, regs->ARM_cpsr);
if (handle_cc(regs, cc)) {
/* Update the link register with the return address 8/
regs->ARM_lr = regs->ARM_pc + 4;
/* Update the CPSR is this is an 'exchange' */
regs->ARM_cpsr = (regs->ARM_cpsr & (~32)) | ((regs->uregs[rm] & 1) << 5);
/* Jump to the register value */
regs->ARM_pc = regs->uregs[rm] & 0xfffffffe;
} else {
/* If the condition code fail, just go to the next instruction. */
regs->ARM_pc += 4;
}
return 0;
}
static struct undef_hook blxv2_hook = {
.instr_mask = 0x0ffffff0,
.instr_val = 0x012fff30,
.cpsr_mask = PSR_T_BIT,
.cpsr_val = 0,
.fn = blxv2_trap,
};
After this, success! Hello world ran correctly. Of course this emulation isn't going to be particularly fast, but it is still infinitely faster than not running at all. (Well, OK, not really, divide by zero is undefined, not infinite.) At this point we were feeling pretty good with ourselves. At this point I must acknowledge Carl and Matt for there assistance with this.
So now I really thought I was home free, but wrong once again. (A
pattern emerging maybe?) So first a bit of a primer on ARM's Thumb
mode (so punny!). ARM has two different instruction sets, the
ARM instruction set, and the Thumb
instruction set. The Thumb instruction set is a 16-bit instruction
set, which has a higher code density than the ARM instruction set.
Now the neat thing about this is that you can actually combine both
ARM and Thumb instruction in the same program. So if your
compiler is smart, it should be able to use both instruction sets for
optimisation. The CPU knows whether code is executing in ARM or Thumb
mode by a bit in the CPSR register. When the bit is set the
instruction stream is assumed to be 16-bit Thumb instruction. Now if
you are running in ARM mode, and want to enter Thumb mode, you need to
do an exchange operation, which is part of the
bx and blx instructions. Now it turns out
that Android is compiled with Thumb mode, so this means it uses
blx to switch from ARM to Thumb mode. So at this stage
I ended up needing to implement blx (version 1)
function. This is shown below:
static int blxv1_trap(struct pt_regs *regs, unsigned int instr)
{
int h = (instr >> 24) & 1;
long imm = ((instr & 0xffffff) << 8);
/* should be signed extended */
imm = imm >> 8;
imm = imm << 2;
imm = imm | (h << 1);
printk("emulate blxv1: %x imm=%lx @ %p\n", instr, imm, (void*) regs->ARM_pc);
regs->ARM_lr = regs->ARM_pc + 4;
regs->ARM_cpsr = regs->ARM_cpsr | (1 << 5);
regs->ARM_pc += imm;
return 0;
}
static struct undef_hook blxv1_hook = {
.instr_mask = 0xfe000000,
.instr_val = 0xfa000000,
.cpsr_mask = PSR_T_BIT,
.cpsr_val = 0,
.fn = blxv1_trap,
};
Now, we get a bit further. But still no go. It turns out that Thumb also has
a new BLX instruction in V5. So, we have to go through and emulate
this instruction for Thumb as well. Below is the code for that.
static int blxv1_t_trap(struct pt_regs *regs, unsigned int instr)
{
u16 offset_11 = instr & 0x7ff;
u16 h = (instr >> 11) & 3;
printk("blx thumb %lx ofs: %lx h: %d @ %p\n",
instr, offset_11, h, (void*) regs->ARM_pc);
if (h == 2) {
long imm = (offset_11 << 12) << 9;
imm = imm >> 9;
regs->ARM_lr = regs->ARM_pc + (imm << 12);
regs->ARM_pc = regs->ARM_pc + 2;
} else {
long new_pc = regs->ARM_lr + (offset_11 << 1);
/* We set the top bit for mega hack! */
regs->ARM_lr = (regs->ARM_pc + 2) | 1;
regs->ARM_pc = new_pc;
if (h == 1) {
regs->ARM_lr = (1 << 31) | (((regs->ARM_lr & 2) >> 1) << 30) | regs->ARM_lr;
regs->ARM_pc = regs->ARM_pc & 0xfffffffc;
regs->ARM_cpsr = regs->ARM_cpsr & (~32);
}
}
printk(" blx thumb after: pc: %lx lr: %lx cpsr: %lx\n", regs->ARM_pc, regs->ARM_lr, regs->ARM_cpsr);
return 0;
}
static struct undef_hook blxv1_t_hook = {
.instr_mask = 0xe000,
.instr_val = 0xe000,
.cpsr_mask = PSR_T_BIT,
.cpsr_val = PSR_T_BIT,
.fn = blxv1_t_trap,
};
Now if you are still with me, and actually read the code, you might
recognise some pretty interesting code. Spot it? No? OK, so the
problem is the way in which ARM code returns to Thumb mode. The
blx instruction updates the link register with the return
address. In Thumb mode it also sets the lowest bit. This ensures that
when bx is called from ARM mode it will jump back into
Thumb mode. It turns out that having to use bx to return
from functions is a bit of a pain, so in ARMv5, the architecture was
updated so that if you popped values from the stack into the
pc register, the CPU would also check the low bit and
switch to Thumb mode if required. Unfortunately ARMv4 doesn't do this.
Rather than checking the lower bit, it simply ignores it and masks
it off, which means you jump back to the return address but remain
in ARM mode, so you end up executing 16-bit instructions as though
they were 32-bit instructions. It may not surprise you to learn that
this generally doesn't work so well.
Which gets us to the truly evil code found above. As well as setting the low bit, we also go and set the top bit of the LR. When the ARM code returns from the function, rather than going to the correct location, it ends up at an unmapped location, which causes a pre-fetch abort. The prefetch abort handler was then updated to handle this error case.
asmlinkage void __exception
do_PrefetchAbort(unsigned long addr, struct pt_regs *regs)
{
if (addr >> 31) {
printk("Magic prefetch abort happened on: %x\n", addr);
regs->ARM_pc = addr & 0x3ffffffe | ((addr >> 29) & 2);
printk(" jumping to : %x\n", regs->ARM_pc);
/* Enable thumb mode */
regs->ARM_cpsr = (regs->ARM_cpsr | 32);
return;
}
do_translation_fault(addr, 0, regs);
}
Now, at this stage, we have something pretty hacked up, but all
these hacks are pretty solid. Unfortunately it still doesn't work. We
have successfully ensured that ARM code returns correctly when called
from Thumb mode, what we have failed to do is ensure that Thumb code
returns correct to ARM code. In ARMv4, this is only possible through
the bx instruction, which correctly sets the Thumb bit,
in the CPSR. Unfortunately on ARMv5, the pop instruction
was extended to also correctly update the thumb bit. But we aren't on
an ARMv5, so it is simply ignored. Which means we get stuck in Thumb
mode and can't correctly return to ARM code.
The prefetch abort trick works to an extent the other way as well, e.g:
for getting from Thumb, back into ARM, but it relies on the ARM code
using the blx instruction. Unfortunately this isn't always
the case, and it is perfectly reasonably for code to use a bl
followed by a bx. As none of these trap it is not possible to
put our magic fake value into the LR register.
The only other option left at this stage is some kind of code
scanning technique. In this we scan the object code looking for the
unsafe pop instructions, and replace them with undefined
instruction so that we safely emulate them with the ARMv5
behaviour. Unfortunately ARM makes this approach basically impossible.
It is not possible to tell if any block of code is Thumb or ARM
instructions. More importantly, it is impossible to determine if a
random word in the text segment is actually an instruction, or is in
fact a literal value. Simply scanning for pop could
actually modify some constants, which would lead to potentially subtle
bugs. If ARM had separate execute and read permissions we could use
the MMU to distinguish between code and data, but unfortunately the ARM
MMU can't really do this. Which means that this approach is basically a
no-go, at least not without some pretty nasty
heuristics, or some really awesome static analysis. Of course we could
just emulate every instruction, but this isn't exactly appealing to me.
(And the performance would really suck!)
In summary, Android is compiled for ARMv5, Neo 1937 is ARMv4. These instruction sets are not compatible. Therefore Android will not run on the Neo 1937. Solutions to this problem would be either:
My guess is none of those three things is going to happen any time soon (although I'll be really happy to be disproved!), so it is better to focus on trying to get this running on an actual ARMv5 based chipset. (E.g: PXA270, i.MX21).
Finally, thanks to Jaq, Carl, David and Matt for providing inspiration and advice.
Update: Thanks to andrzej for spotting the bug in my clz() emulation. It should of course be 32 - fls(), not fls(). This is now updated.
Last
time I looked at the processes involved with initialising the
Android Java framework. This wasn't able to give us much information
on what was happening. To get some more data we are going to be using
the strace tool. I've
compiled a version of strace for Android,
which you can download and use.
This time we start runtime using strace:
./strace -ff -F -tt -s 200 -o /tmp/strace runtime.
-ff makes strace follow fork() and output
each forked files trace to a different file. -F means we
try and follow any vfork()s. -tt prints out
the time of system calls in microseconds. -s 200 so that
we can see a bit more detail in any strings that are used. After doing this
we also start up zygote.
After start-up we can get a little bit more information about the
running process. When we dig into the /proc/ directory of
our runtime task we find a bit more information. In particular we see
that as well as the main process, there are two extra threads. Now we
are in a good position to start examining the trace files. We copy these
across to the host using adb pull to make analysis a little
easier.
We start by looking at the main process. Below is the annotate trace of the main thread.
The first part is just standard process initialisation:
05:30:51.171267 execve("/system/bin/runtime", ["runtime"], [/* 8 vars */]) = 0
05:30:51.179335 getpid() = 493
05:30:51.180403 syscall_983045(0xb0016b68, 0xb0013780, 0x3e4, 0, 0xbef86e58, 0x1, 0, 0xf0005, 0xb0013780, 0, 0, 0xbef86e54, 0, 0xbef86e08, 0xb0000d89, 0xb00016ec, 0x10, 0xb0016b68, 0, 0, 0xc764, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) = 0
05:30:51.181639 gettid() = 493
05:30:51.182628 sigaction(SIGILL, {0xb0001469, [], SA_RESTART}, {SIG_DFL}, 0) = 0
05:30:51.184487 sigaction(SIGABRT, {0xb0001469, [], SA_RESTART}, {SIG_DFL}, 0) = 0
05:30:51.186137 sigaction(SIGBUS, {0xb0001469, [], SA_RESTART}, {SIG_DFL}, 0) = 0
05:30:51.187460 sigaction(SIGFPE, {0xb0001469, [], SA_RESTART}, {SIG_DFL}, 0) = 0
05:30:51.188726 sigaction(SIGSEGV, {0xb0001469, [], SA_RESTART}, {SIG_DFL}, 0) = 0
05:30:51.189968 sigaction(SIGSTKFLT, {0xb0001469, [], SA_RESTART}, {SIG_DFL}, 0) = 0
Next up is the loading of shared libraries. I've stripped this down after the first one, as it is just the same blocks of code with different actual libraries. It doesn't quite load all the libraries on the system, but it goes pretty close:
05:30:51.191422 open("libandroid_runtime.so", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
05:30:51.193687 open("/system/lib/libandroid_runtime.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.195163 lseek(4, -8, SEEK_END) = 313648
05:30:51.196653 read(4, "\0\0000\255PRE ", 8) = 8
05:30:51.198031 mmap2(0xad300000, 315392, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE, 4, 0) = 0xad300000
05:30:51.199144 close(4) = 0
05:30:51.202026 mmap2(0xad34d000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0) = 0xad34d000
...
...
05:30:51.205846 open("/system/lib/libexpat.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.212147 open("/system/lib/libc.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.222236 open("/system/lib/libstdc++.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.228944 open("/system/lib/libm.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.237093 open("/system/lib/libnativehelper.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.246900 open("/system/lib/libcutils.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.257238 open("/system/lib/libssl.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.266356 open("/system/lib/libcrypto.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.285991 open("/system/lib/libutils.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.292459 open("/system/lib/libz.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.314445 open("/system/lib/libicudata.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.323224 open("/system/lib/libicuuc.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.339731 open("/system/lib/libicui18n.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.366170 open("/system/lib/libsqlite.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.384451 open("/system/lib/libui.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.394162 open("/system/lib/libcorecg.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.403491 open("/system/lib/libpixelflinger.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.411948 open("/system/lib/libhardware.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.419561 open("/system/lib/libmedia.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.491956 open("/system/lib/libsgl.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.517146 open("/system/lib/libdvm.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.534603 open("/system/lib/libpim.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.542365 open("/system/lib/libopengles_cm.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.554764 open("/system/lib/libsonivox.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.566665 open("/system/lib/libdbus.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.602983 open("/system/lib/libsystem_server.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.610384 open("/system/lib/libsurfaceflinger.so", O_RDONLY|O_LARGEFILE) = 4
05:30:51.652278 open("/system/lib/libaudioflinger.so", O_RDONLY|O_LARGEFILE) = 4
Not quite sure what this stuff does. Nothing too important though.
05:30:51.698931 gettid() = 493 05:30:51.699882 syscall_983045(0xbef86d5c, 0, 0x20, 0, 0xbef67000, 0xbef86e10, 0xbef86e50, 0xf0005, 0xbef86e50, 0, 0xd9e8, 0xd9e4, 0, 0xbef86d50, 0xafe20a4b, 0xafe0949c, 0x60000010, 0xbef86d5c, 0, 0, 0xc764, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) = 0
This next bit is interesting. We open a socket, but the path is a little interesting. Definitely needs some more investigation.
05:30:51.701110 socket(PF_FILE, SOCK_STREAM, 0) = 4
05:30:51.702175 connect(4, {sa_family=AF_FILE, path=@property_service}, 19) = 0
05:30:51.703602 recvmsg(4, {msg_name(0)=NULL, msg_iov(1)=[{"\0\200\0\0", 4}], msg_controllen=16, {cmsg_len=16, cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS, {5}}, msg_flags=0}, 0) = 4
05:30:51.704618 mmap2(NULL, 32768, PROT_READ, MAP_SHARED, 5, 0) = 0x40000000
05:30:51.705794 close(5) = 0
05:30:51.706714 close(4) = 0
Try to open a file for malloc debug. It might be interesting to actually create this file and seem what happens.
05:30:51.708084 open("/data/malloc_debug", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
Something did a malloc presumably, causing the brk.
05:30:51.709572 brk(0x15000) = 0x15000
Scan the /tmp directory for some reason.
05:30:51.710607 open("/tmp", O_RDONLY|O_LARGEFILE|O_DIRECTORY) = 4
05:30:51.711709 getdents64(4, /* 6 entries */, 4200) = 192
05:30:51.714895 getdents64(4, /* 0 entries */, 4200) = 0
05:30:51.716089 close(4) = 0
It opens up a bunch of log files.
05:30:51.717808 open("/dev/log/main", O_WRONLY|O_LARGEFILE) = 4
05:30:51.719025 open("/dev/log/radio", O_WRONLY|O_LARGEFILE) = 5
05:30:51.720213 open("/dev/log/events", O_WRONLY|O_LARGEFILE) = 6
Write out a startup message
05:30:51.721375 writev(4, [{"\4", 1}, {"runtime\0", 8}, {"*** Startup: root=\'/system\' asset=\'/system/app\' data=\'/data\' ***\n\0", 66}], 3) = 75
Check the input device is available, but don't open it.
05:30:51.723733 access("/dev/input/event0", R_OK) = 0
05:30:51.725407 writev(4, [{"\4", 1}, {"runtime\0", 8}, {"Key input device: 1 1 \'/dev/input/event0\'\n\0", 43}], 3) = 52
05:30:51.727398 getpid() = 493
05:30:51.728306 setpgid(0, 493) = 0
05:30:51.729234 chdir("/system/app") = 0
05:30:51.730480 open("/dev/alarm", O_RDWR|O_LARGEFILE) = 7
05:30:51.731637 ioctl(7, 0x40086104, 0xbef86ce0) = 0
05:30:51.732909 close(7) = 0
This bit here is pretty important. /dev/binder is the character
device for OpenBinder, which is something that you see in the kernel source. This
appears to be an IPC sub-system for the Linux kernel. It was at some stage
donated by Palm, and there was an OpenBinder.org website for sometime, although that
site is now dead. Finding more information on this will be essential.
05:30:51.733954 open("/dev/binder", O_RDWR|O_LARGEFILE) = 7
05:30:51.735488 fcntl64(7, F_SETFD, FD_CLOEXEC) = 0
05:30:51.736483 ioctl(7, 0xc0046209, 0xbef86cbc) = 0
05:30:51.737443 ioctl(7, 0x40046205, 0xbef86cb8) = 0
05:30:51.738385 mmap2(NULL, 8388608, PROT_READ, MAP_PRIVATE|MAP_NORESERVE, 7, 0) = 0x40008000
05:30:51.739478 writev(4, [{"\4", 1}, {"runtime\0", 8}, {"Entered boot_init()!\n\0", 22}], 3) = 31
05:30:51.741301 writev(4, [{"\3", 1}, {"runtime\0", 8}, {"ProcessState: 0x13420\n\0", 23}], 3) = 32
05:30:51.743109 ioctl(7, 0x40046207, 0xbef86c9c) = 0
05:30:51.744223 writev(4, [{"\4", 1}, {"runtime\0", 8}, {"Binder driver opened. Multiprocess enabled.\n\0", 46}], 3) = 55
05:30:51.746874 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(ui_hardware, 0x1357c)\n\0", 50}], 3) = 66
A loop trying to communicate with zygote.
05:30:51.749204 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
05:30:51.750298 socket(PF_FILE, SOCK_STREAM, 0) = 8
05:30:51.751305 connect(8, {sa_family=AF_FILE, path=@android.zygote}, 17) = -1 ECONNREFUSED (Connection refused)
05:30:51.752442 close(8) = 0
...
05:30:51.753446 nanosleep({0, 500000000}, {0, 500000000}) = 0
05:30:52.266564 socket(PF_FILE, SOCK_STREAM, 0) = 8
05:30:52.268061 connect(8, {sa_family=AF_FILE, path=@android.zygote}, 17) = -1 ECONNREFUSED (Connection refused)
05:30:52.269456 close(8) = 0
...
05:31:04.230401 nanosleep({0, 500000000}, {0, 500000000}) = 0
05:31:04.746608 socket(PF_FILE, SOCK_STREAM, 0) = 8
05:31:04.748192 connect(8, {sa_family=AF_FILE, path=@android.zygote}, 17) = 0
Once a connection with zygote is obtained it appears to send a message:
5.
It then seems to receive back a 4 byte message, which seems to be 498, but it's not clear
what that is. My guess would be a pid, but it doesn't seem to be true. It seems that
--allow-disconnect
--nice-name=system_server
--setuid=0
--setgid=0
android.server.SystemServerzygote acts as a process manager, and runtime can communicate with it
to start new processes, in particular it starts up the system_server
process.
05:31:04.749657 fcntl64(8, F_GETFL) = 0x2 (flags O_RDWR)
05:31:04.751247 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40808000
05:31:04.752834 mprotect(0x40808000, 4096, PROT_READ) = 0
05:31:04.754234 fstat64(8, {st_mode=S_IFSOCK|0777, st_size=0, ...}) = 0
05:31:04.757578 mprotect(0x40808000, 4096, PROT_READ|PROT_WRITE) = 0
05:31:04.758739 mprotect(0x40808000, 4096, PROT_READ) = 0
05:31:04.759853 write(8, "5\n--allow-disconnect\n--nice-name=system_server\n--setuid=0\n--setgid=0\nandroid.server.SystemServer\n", 97) = 97
05:31:04.762081 read(8, "\0", 1024) = 1
05:31:05.818220 read(8, "\0", 1024) = 1
05:31:05.820120 read(8, "\1", 1024) = 1
05:31:05.821236 read(8, "\362", 1024) = 1
05:31:05.822320 close(8) = 0
Initialise and print out messages on the console.
05:31:05.823445 fstat64(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(136, 0), ...}) = 0
05:31:05.825556 mprotect(0x40808000, 4096, PROT_READ|PROT_WRITE) = 0
05:31:05.826475 mprotect(0x40808000, 4096, PROT_READ) = 0
05:31:05.827278 ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
05:31:05.828818 write(1, "+++ post-zygote\n", 16) = 16
Create a new thread -- we find out what this does in a little bit.
05:31:05.832934 mmap2(0x10000000, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x10000000 05:31:05.833889 mprotect(0x10000000, 4096, PROT_NONE) = 0 05:31:05.834890 clone(child_stack=0x100fff80, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_DETACHED) = 484
Not quite sure what is going on here. There appears to be communication with another thread (the SurfaceFlinger) via the futex 0x134a8, it then prints a message once it has found the SurfaceFlinger.
05:31:05.837944 writev(4, [{"\3", 1}, {"runtime\0", 8}, {"DisplayDevice::detach\n\0", 23}], 3) = 32
05:31:05.839713 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: waiting for service SurfaceFlinger\n\0", 52}], 3) = 68
05:31:05.841621 clock_gettime(CLOCK_MONOTONIC, {127, 101400000}) = 0
05:31:05.842936 clock_gettime(CLOCK_MONOTONIC, {127, 103070000}) = 0
05:31:05.844238 clock_gettime(CLOCK_MONOTONIC, {127, 104026000}) = 0
05:31:05.845441 futex(0x134ac, FUTEX_WAIT, -1, {4, 997000000}) = 0
05:31:06.118721 futex(0x134a8, FUTEX_WAIT, 2, NULL) = -1 EAGAIN (Resource temporarily unavailable)
05:31:06.122382 futex(0x134a8, FUTEX_WAKE, 1) = 0
05:31:06.126330 writev(4, [{"\4", 1}, {"runtime\0", 8}, {"Display startup: 1 devices\n\0", 28}], 3) = 37
No time like the present to get your PID and UID. No idea why this is required (twice!).
05:31:06.133210 getpid() = 493 05:31:06.154288 getuid32() = 0 05:31:06.160787 getpid() = 493 05:31:06.165845 getuid32() = 0
This block appears to be an IPC using the OpenBinder interface. This is a common block of code that we see throughout. Not quite sure why the getpriority is required every-time. It would be extremely useful to see the contents of the ioctl buffer, but this isn't really available easily in strace. It might mean either extensions to strace, or putting some hacks in the kernel.
05:31:06.168510 getpriority(PRIO_PROCESS, 0) = 20 05:31:06.171049 ioctl(7, 0xc0186201, 0xbef86b08) = 0 05:31:06.187776 ioctl(7, 0xc0186201, 0xbef86b08) = 0
More log file activity. Here we seem to have detached from the display device, presumably that was what the previous IPC was all about.
05:31:06.189902 writev(4, [{"\3", 1}, {"runtime\0", 8}, {"DisplayDevice::shutDown\n\0", 25}], 3) = 34
05:31:06.191663 writev(4, [{"\3", 1}, {"runtime\0", 8}, {"DisplayDevice::detach\n\0", 23}], 3) = 32
This next section seems to be waiting for service activity to occur.
05:31:06.193425 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: waiting for service activity\n\0", 46}], 3) = 62
05:31:06.271693 clock_gettime(CLOCK_MONOTONIC, {127, 531615000}) = 0
05:31:06.272800 clock_gettime(CLOCK_MONOTONIC, {127, 532606000}) = 0
05:31:06.273769 clock_gettime(CLOCK_MONOTONIC, {127, 533559000}) = 0
05:31:06.274735 futex(0x134ac, FUTEX_WAIT, -3, {4, 998000000}) = 0
05:31:06.800315 futex(0x134a8, FUTEX_WAIT, 2, NULL) = -1 EAGAIN (Resource temporarily unavailable)
05:31:06.802159 clock_gettime(CLOCK_MONOTONIC, {128, 61969000}) = 0
05:31:06.804065 clock_gettime(CLOCK_MONOTONIC, {128, 63928000}) = 0
05:31:06.879426 futex(0x134a8, FUTEX_WAKE, 1) = 0
05:31:06.882042 futex(0x134ac, FUTEX_WAIT, -4, {4, 467000000}) = 0
05:31:06.891935 futex(0x134a8, FUTEX_WAIT, 2, NULL) = -1 EAGAIN (Resource temporarily unavailable)
05:31:06.893777 futex(0x134a8, FUTEX_WAKE, 1) = 0
05:31:06.898754 getpid() = 493
05:31:06.904418 getuid32() = 0
05:31:06.907269 getpriority(PRIO_PROCESS, 0) = 20
05:31:06.909036 ioctl(7, 0xc0186201, 0xbef86b58) = 0
05:31:06.914561 ioctl(7, 0xc0186201, 0xbef86b58) = 0
05:31:06.920828 futex(0xafe40c00, FUTEX_WAIT, 2, NULL) = -1 EAGAIN (Resource temporarily unavailable)
05:31:06.923679 futex(0xafe40c00, FUTEX_WAKE, 1) = 0
05:31:06.928106 ioctl(7, 0xc0186201, 0xbef86c10) = 0
Now that service activity is ready to happen, we go through a series of adding services.
05:31:06.933366 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(activity.services, 0x150d0)\n\0", 56}], 3) = 72
05:31:06.939660 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
05:31:06.940605 getpriority(PRIO_PROCESS, 0) = 25
05:31:06.941485 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:06.946561 ioctl(7, 0xc0186201, 0xbef86c10) = 0
Unknown IPC
05:31:08.201642 getpriority(PRIO_PROCESS, 0) = 35 05:31:08.202648 ioctl(7, 0xc0186201, 0xbef86ac0) = 0 05:31:08.205743 ioctl(7, 0xc0186201, 0xbef86c10) = 0
Add the window service
05:31:08.223890 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(window, 0x15418)\n\0", 45}], 3) = 61
05:31:08.226533 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
05:31:08.227542 getpriority(PRIO_PROCESS, 0) = 25
05:31:08.228462 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:08.231366 ioctl(7, 0xc0186201, 0xbef86c10) = 0
Add the USB service
05:31:08.339061 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(USB, 0x151f8)\n\0", 42}], 3) = 58
05:31:08.341890 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
05:31:08.342755 getpriority(PRIO_PROCESS, 0) = 25
05:31:08.343562 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:08.366177 ioctl(7, 0xc0186201, 0xbef86c10) = 0
Unknown IPC
05:31:08.470072 getpriority(PRIO_PROCESS, 0) = 25 05:31:08.471010 ioctl(7, 0xc0186201, 0xbef86ac0) = 0 05:31:08.471990 ioctl(7, 0xc0186201, 0xbef86c10) = 0
Add the statusbar service.
05:31:08.481632 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(statusbar, 0x15660)\n\0", 48}], 3) = 64
05:31:08.483719 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
05:31:08.484601 getpriority(PRIO_PROCESS, 0) = 25
05:31:08.485720 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:08.491172 ioctl(7, 0xc0186201, 0xbef86c10) = 0
Unknown IPCs
05:31:08.499888 getpriority(PRIO_PROCESS, 0) = 25 05:31:08.501478 ioctl(7, 0xc0186201, 0xbef86ac0) = 0 05:31:08.505934 ioctl(7, 0xc0186201, 0xbef86c10) = 0 05:31:08.514132 getpriority(PRIO_PROCESS, 0) = 25 05:31:08.518759 ioctl(7, 0xc0186201, 0xbef86ac0) = 0 05:31:08.523850 ioctl(7, 0xc0186201, 0xbef86c10) = 0
Add the statusbar service.
05:31:08.530638 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(statistics, 0x15890)\n\0", 49}], 3) = 65
05:31:08.533406 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
05:31:08.534278 getpriority(PRIO_PROCESS, 0) = 25
05:31:08.535099 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:08.626083 ioctl(7, 0xc0186201, 0xbef86c10) = 0
Unknown IPCs
05:31:08.868326 getpriority(PRIO_PROCESS, 0) = 20 05:31:08.869383 ioctl(7, 0xc0186201, 0xbef86ac0) = 0 05:31:08.870513 ioctl(7, 0xc0186201, 0xbef86c10) = 0 05:31:08.874043 getpriority(PRIO_PROCESS, 0) = 20 05:31:08.875021 ioctl(7, 0xc0186201, 0xbef86ac0) = 0 05:31:08.877895 ioctl(7, 0xc0186201, 0xbef86c10) = 0 05:31:08.930930 getpriority(PRIO_PROCESS, 0) = 20 05:31:08.931941 ioctl(7, 0xc0186201, 0xbef86ac0) = 0 05:31:08.932985 ioctl(7, 0xc0186201, 0xbef86c10) = 0 05:31:08.949753 getpriority(PRIO_PROCESS, 0) = 20 05:31:08.950735 ioctl(7, 0xc0186201, 0xbef86ac0) = 0 05:31:08.951764 ioctl(7, 0xc0186201, 0xbef86c10) = 0 05:31:09.065015 getpriority(PRIO_PROCESS, 0) = 20 05:31:09.066231 ioctl(7, 0xc0186201, 0xbef86ac0) = 0 05:31:09.067293 ioctl(7, 0xc0186201, 0xbef86c10) = 0 05:31:09.122422 getpriority(PRIO_PROCESS, 0) = 20 05:31:09.123511 ioctl(7, 0xc0186201, 0xbef86ac0) = 0 05:31:09.124627 ioctl(7, 0xc0186201, 0xbef86c10) = 0 05:31:09.165747 getpriority(PRIO_PROCESS, 0) = 20 05:31:09.166886 ioctl(7, 0xc0186201, 0xbef86ac0) = 0 05:31:09.167966 ioctl(7, 0xc0186201, 0xbef86c10) = 0 05:31:09.206344 getpriority(PRIO_PROCESS, 0) = 20 05:31:09.207418 ioctl(7, 0xc0186201, 0xbef86ac0) = 0 05:31:09.208536 ioctl(7, 0xc0186201, 0xbef86c10) = 0
Add the PhoneNotifier service
05:31:09.428385 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(android.telephony.PhoneNotifier, 0x15928)\n\0", 70}], 3) = 86
05:31:09.431266 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
05:31:09.432328 getpriority(PRIO_PROCESS, 0) = 20
05:31:09.433284 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:09.434453 ioctl(7, 0xc0186201, 0xbef86c10) = 0
Unknown IPC
05:31:09.449402 getpriority(PRIO_PROCESS, 0) = 20 05:31:09.450514 ioctl(7, 0xc0186201, 0xbef86ac0) = 0 05:31:09.451817 ioctl(7, 0xc0186201, 0xbef86c10) = 0
05:31:09.524328 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(SMS, 0x15988)\n\0", 42}], 3) = 58
05:31:09.526862 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
05:31:09.527847 getpriority(PRIO_PROCESS, 0) = 20
05:31:09.528758 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:09.529847 ioctl(7, 0xc0186201, 0xbef86c10) = 0
Add the SimPhoneBook service
05:31:09.549454 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(android.telephony.SimPhoneBook, 0x154c0)\n\0", 69}], 3) = 85
05:31:09.552176 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
05:31:09.553202 getpriority(PRIO_PROCESS, 0) = 20
05:31:09.554154 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:09.556977 ioctl(7, 0xc0186201, 0xbef86c10) = 0
Unknown IPC
05:31:09.704285 getpriority(PRIO_PROCESS, 0) = 20 05:31:09.705523 ioctl(7, 0xc0186201, 0xbef86ac0) = 0 05:31:09.706634 ioctl(7, 0xc0186201, 0xbef86c10) = 0 05:31:09.867287 getpriority(PRIO_PROCESS, 0) = 20 05:31:09.868394 ioctl(7, 0xc0186201, 0xbef86ac0) = 0 05:31:09.869523 ioctl(7, 0xc0186201, 0xbef86c10) = 0
Add the volume service.
05:31:09.886578 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(volume, 0x15540)\n\0", 45}], 3) = 61
05:31:09.888998 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
05:31:09.890025 getpriority(PRIO_PROCESS, 0) = 20
05:31:09.890977 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:09.892075 ioctl(7, 0xc0186201, 0xbef86c10) = 0
Add the phone service.
05:31:09.910618 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(phone, 0x155b8)\n\0", 44}], 3) = 60
05:31:09.912975 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
05:31:09.913996 getpriority(PRIO_PROCESS, 0) = 20
05:31:09.914986 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:09.917974 ioctl(7, 0xc0186201, 0xbef86c10) = 0
Unknown IPCs
05:31:09.941316 getpriority(PRIO_PROCESS, 0) = 20 05:31:09.942419 ioctl(7, 0xc0186201, 0xbef86ac0) = 0 05:31:09.943557 ioctl(7, 0xc0186201, 0xbef86c10) = 0 05:31:09.948824 getpriority(PRIO_PROCESS, 0) = 20 05:31:09.949879 ioctl(7, 0xc0186201, 0xbef86ac0) = 0 05:31:09.950985 ioctl(7, 0xc0186201, 0xbef86c10) = 0
Bluetooth service
05:31:10.141405 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(org.bluez.bluetooth, 0x15ca8)\n\0", 58}], 3) = 74
05:31:10.144074 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
05:31:10.145156 getpriority(PRIO_PROCESS, 0) = 20
05:31:10.146278 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:10.147366 ioctl(7, 0xc0186201, 0xbef86c10) = 0
Add bluetooth mailbox service
05:31:10.150666 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(org.bluez.bluetooth_mailbox, 0x15d50)\n\0", 66}], 3) = 82
05:31:10.153207 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
05:31:10.154173 getpriority(PRIO_PROCESS, 0) = 20
05:31:10.155077 ioctl(7, 0xc0186201, 0xbef86ac0) = 0
05:31:10.158028 ioctl(7, 0xc0186201, 0xbef86c10) = 0
Many more unknown IPCs
..... 05:31:12.209289 getpriority(PRIO_PROCESS, 0) = 20 05:31:12.210336 ioctl(7, 0xc0186201, 0xbef86ac0) = 0 05:31:12.213804 ioctl(7, 0xc0186201
At the end of time, the runtime sits waiting for more IPCs to occur. That ends the analysis of the main runtime thread. In summary it seems to hang around as the ServiceManager, adding services. Presumably the other IPCs relate to other threads requesting services and the like. Before doing this it seems to help kick things off by spawning android.server.SystemServer.
Along the way you may remember that a new thread was spawned (thread id 484). We now take a look at what happens in that thread. It seems to do not very much. It has some interaction with the binder, and then seems to just spawn off a new thread of its own before exiting.
06:38:07.140035 syscall_983045(0x100fff80, 0, 0x20, 0, 0x100fff80, 0x139f0, 0xa9d25ef9, 0xf0005, 0xbeaacc38, 0xafe36e70, 0xa9d25ef9, 0x1, 0, 0x100fff68, 0xafe0b238, 0xafe0949c, 0x60000010, 0x100fff80, 0xc1000, 0, 0xc764, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) = 0 06:38:07.141657 getpid() = 479 06:38:07.142520 getuid32() = 0 06:38:07.143438 ioctl(7, 0xc0186201, 0x100ffef8) = 0 06:38:07.305190 mmap2(0x10100000, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x10100000 06:38:07.306160 mprotect(0x10100000, 4096, PROT_NONE) = 0 06:38:07.307099 clone(child_stack=0x101fff80, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_DETACHED) = 488 06:38:07.308664 getpriority(PRIO_PROCESS, 0) = 20 06:38:07.309560 ioctl(7, 0xc0186201, 0x100ffda8) = 0 06:38:07.310866 ioctl(7, 0xc0186201, 0x100ffef8) = 0 06:38:15.193301 ioctl(7, 0xc0186201, 0x100ffef8) = 0 06:38:15.194829 futex(0x13a00, FUTEX_WAKE, 1) = 0 06:38:15.196296 munmap(0x10000000, 1048576) = 0 06:38:15.198228 exit(0) = ?
The next thread is 488. Like the last thread, it seems to just create a new thread (499), and then exit. (Not quite sure why we have these threads that start, do not much and then exit, maybe implementing some asynchronous style IPC(?).
06:38:07.311899 syscall_983045(0x101fff80, 0, 0x20, 0, 0x101fff80, 0x13d50, 0xa9d25ef9, 0xf0005, 0x100ffd90, 0xafe36e70, 0xa9d25ef9, 0x1, 0, 0x101fff68, 0xafe0b238, 0xafe0949c, 0x60000010, 0x101fff80, 0xc10f0, 0, 0xc764, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) = 0
06:38:07.313122 getpid() = 479
06:38:07.313959 getuid32() = 0
06:38:07.314808 ioctl(7, 0xc0186201, 0x101ffef8) = 0
06:38:07.370653 mmap2(0x10200000, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x10200000
06:38:07.371719 mprotect(0x10200000, 4096, PROT_NONE) = 0
06:38:07.372785 clone(child_stack=0x102fff80, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_DETACHED) = 499
06:38:07.374282 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: waiting for service SurfaceFlinger\n\0", 52}], 3) = 68
06:38:07.377988 clock_gettime(CLOCK_MONOTONIC, {41, 594807000}) = 0
06:38:07.379786 clock_gettime(CLOCK_MONOTONIC, {41, 596593000}) = 0
06:38:07.381511 clock_gettime(CLOCK_MONOTONIC, {41, 598312000}) = 0
06:38:07.383379 futex(0x134ac, FUTEX_WAIT, -1, {4, 996000000}) = 0
06:38:07.401561 futex(0x134a8, FUTEX_WAIT, 2, NULL) = -1 EAGAIN (Resource temporarily unavailable)
06:38:07.405316 futex(0x134a8, FUTEX_WAKE, 1) = 0
06:38:07.409904 getpriority(PRIO_PROCESS, 0) = 30
06:38:07.416223 ioctl(7, 0xc0186201, 0x101ffda8) = 0
06:38:07.440719 ioctl(7, 0xc0186201, 0x101ffef8) = 0
06:38:25.193171 ioctl(7, 0xc0186201, 0x101ffef8) = 0
06:38:25.194719 futex(0x13d60, FUTEX_WAKE, 1) = 0
06:38:25.196134 munmap(0x10100000, 1048576) = 0
06:38:25.198048 exit(0) = ?
And now thread 499. This seems to spawn another thread (TID 500), adds the SurfaceFlinger service, and then exit.
06:38:07.376978 syscall_983045(0x102fff80, 0, 0x20, 0, 0x102fff80, 0x14060, 0xa9d25ef9, 0xf0005, 0x101ffd90, 0xafe36e70, 0xa9d25ef9, 0x1, 0, 0x102fff68, 0xafe0b238, 0xafe0949c, 0x60000010, 0x102fff80, 0, 0, 0xc764, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) = 0
06:38:07.379192 getpid() = 479
06:38:07.380922 getuid32() = 0
06:38:07.382691 ioctl(7, 0xc0186201, 0x102ffef8) = 0
06:38:07.385026 mmap2(0x10300000, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x10300000
06:38:07.392408 mprotect(0x10300000, 4096, PROT_NONE) = 0
06:38:07.393447 clone(child_stack=0x103fff80, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_DETACHED) = 500
06:38:07.395459 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(SurfaceFlinger, 0x144a0)\n\0", 53}], 3) = 69
06:38:07.398890 futex(0x134ac, FUTEX_WAKE, 2147483647) = 2
06:38:07.402396 futex(0x134a8, FUTEX_WAKE, 1) = 0
06:38:07.406189 getpriority(PRIO_PROCESS, 0) = 20
06:38:07.410508 ioctl(7, 0xc0186201, 0x102ffda8) = 0
06:38:07.433790 ioctl(7, 0xc0186201, 0x102ffef8) = 0
06:38:20.193223 ioctl(7, 0xc0186201, 0x102ffef8) = 0
06:38:20.194793 futex(0x14070, FUTEX_WAKE, 1) = 0
06:38:20.196186 munmap(0x10200000, 1048576) = 0
06:38:20.198087 exit(0) = ?
Thread 500 actually seems to do something useful. This seems to create yet another thread (502), and then seems to do something very similar to the main thread, sitting in a loop adding new services, and responding IPC messages. It is not clear why we need two different threads. Possibly there is a different thread for each possible client.
06:38:07.397343 syscall_983045(0x103fff80, 0, 0x20, 0, 0x103fff80, 0x143b8, 0xa9d25ef9, 0xf0005, 0x102ffd90, 0xafe36e70, 0xa9d25ef9, 0x1, 0, 0x103fff68, 0xafe0b238, 0xafe0949c, 0x60000010, 0x103fff80, 0xc12d0, 0, 0xc764, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) = 0
06:38:07.400080 getpid() = 479
06:38:07.403663 getuid32() = 0
06:38:07.407513 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:07.414539 mmap2(0x10400000, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x10400000
06:38:07.439311 mprotect(0x10400000, 4096, PROT_NONE) = 0
06:38:07.442042 clone(child_stack=0x104fff80, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_DETACHED) = 502
06:38:07.444609 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(AudioFlinger, 0x14b40)\n\0", 51}], 3) = 67
06:38:07.449432 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
06:38:07.452182 getpriority(PRIO_PROCESS, 0) = 20
06:38:07.459755 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:07.473981 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:08.076657 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(power, 0x14830)\n\0", 44}], 3) = 60
06:38:08.078875 futex(0x134ac, FUTEX_WAKE, 2147483647) = 1
06:38:08.080710 futex(0x134a8, FUTEX_WAKE, 1) = 0
06:38:08.082566 getpriority(PRIO_PROCESS, 0) = 25
06:38:08.084402 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:08.099967 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:08.165531 getpriority(PRIO_PROCESS, 0) = 25
06:38:08.166646 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:08.167758 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:08.171184 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(activity, 0x14e88)\n\0", 47}], 3) = 63
06:38:08.173533 futex(0x134ac, FUTEX_WAKE, 2147483647) = 1
06:38:08.177313 futex(0x134a8, FUTEX_WAKE, 1) = 1
06:38:08.185288 getpriority(PRIO_PROCESS, 0) = 25
06:38:08.186450 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:08.187727 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:08.206589 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(activity.receivers, 0x15028)\n\0", 57}], 3) = 73
06:38:08.209646 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
06:38:08.210606 getpriority(PRIO_PROCESS, 0) = 25
06:38:08.211485 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:08.219706 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:08.225865 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(activity.providers, 0x14e30)\n\0", 57}], 3) = 73
06:38:08.228957 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
06:38:08.229950 getpriority(PRIO_PROCESS, 0) = 25
06:38:08.230829 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:08.258759 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:09.049151 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(package, 0x15260)\n\0", 46}], 3) = 62
06:38:09.051523 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
06:38:09.052531 getpriority(PRIO_PROCESS, 0) = 25
06:38:09.053477 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:09.054570 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:09.059698 getpriority(PRIO_PROCESS, 0) = 25
06:38:09.060763 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:09.061861 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:09.083975 getpriority(PRIO_PROCESS, 0) = 25
06:38:09.085589 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:09.086949 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:09.104476 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(content, 0x152a0)\n\0", 46}], 3) = 62
06:38:09.107125 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
06:38:09.108104 getpriority(PRIO_PROCESS, 0) = 25
06:38:09.109013 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:09.118773 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:09.183888 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(battery, 0x15320)\n\0", 46}], 3) = 62
06:38:09.186582 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
06:38:09.187569 getpriority(PRIO_PROCESS, 0) = 25
06:38:09.188464 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:09.225874 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:09.487155 getpriority(PRIO_PROCESS, 0) = 35
06:38:09.488238 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:09.489328 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:09.521681 getpriority(PRIO_PROCESS, 0) = 25
06:38:09.522675 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:09.544021 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:09.601845 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(org.bluez.bluetooth_service, 0x15150)\n\0", 66}], 3) = 82
06:38:09.604465 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
06:38:09.606401 getpriority(PRIO_PROCESS, 0) = 25
06:38:09.607262 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:09.639998 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:09.803186 getpriority(PRIO_PROCESS, 0) = 25
06:38:09.804803 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:09.810019 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:09.819942 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(statistics, 0x15888)\n\0", 49}], 3) = 65
06:38:09.822767 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
06:38:09.823646 getpriority(PRIO_PROCESS, 0) = 25
06:38:09.824478 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:09.865728 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:10.154474 getpriority(PRIO_PROCESS, 0) = 20
06:38:10.157321 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:10.158537 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:10.163674 getpriority(PRIO_PROCESS, 0) = 20
06:38:10.164666 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:10.165885 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:10.251413 getpriority(PRIO_PROCESS, 0) = 20
06:38:10.252438 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:10.253512 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:10.274076 getpriority(PRIO_PROCESS, 0) = 20
06:38:10.276875 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:10.278112 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:10.282103 getpriority(PRIO_PROCESS, 0) = 20
06:38:10.283091 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:10.284126 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:10.318757 getpriority(PRIO_PROCESS, 0) = 20
06:38:10.319829 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:10.320913 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:10.527902 getpriority(PRIO_PROCESS, 0) = 20
06:38:10.529000 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:10.530108 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:10.553191 getpriority(PRIO_PROCESS, 0) = 20
06:38:10.554366 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:10.557334 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:10.785261 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(android.telephony.PhoneNotifier, 0x15938)\n\0", 70}], 3) = 86
06:38:10.787969 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
06:38:10.788952 getpriority(PRIO_PROCESS, 0) = 20
06:38:10.789867 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:10.790925 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:10.825487 getpriority(PRIO_PROCESS, 0) = 20
06:38:10.826620 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:10.827751 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:10.864624 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(SMS, 0x15998)\n\0", 42}], 3) = 58
06:38:10.867192 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
06:38:10.868174 getpriority(PRIO_PROCESS, 0) = 20
06:38:10.869088 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:10.870161 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:10.902914 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(android.telephony.SimPhoneBook, 0x154c0)\n\0", 69}], 3) = 85
06:38:10.905863 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
06:38:10.906910 getpriority(PRIO_PROCESS, 0) = 20
06:38:10.908018 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:10.909213 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:10.964689 getpriority(PRIO_PROCESS, 0) = 20
06:38:10.965983 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:10.967082 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:11.034374 getpriority(PRIO_PROCESS, 0) = 20
06:38:11.038891 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:11.040226 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:11.067389 getpriority(PRIO_PROCESS, 0) = 20
06:38:11.068514 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:11.078629 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:11.253134 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(volume, 0x15540)\n\0", 45}], 3) = 61
06:38:11.258928 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
06:38:11.260032 getpriority(PRIO_PROCESS, 0) = 20
06:38:11.260964 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:11.270195 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:11.284493 getpriority(PRIO_PROCESS, 0) = 20
06:38:11.288630 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:11.298964 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:11.432430 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(org.bluez.bluetooth, 0x15cb8)\n\0", 58}], 3) = 74
06:38:11.434972 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
06:38:11.441544 getpriority(PRIO_PROCESS, 0) = 20
06:38:11.445408 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:11.452511 ioctl(7, 0xc0186201, 0x103ffef8) = 0
06:38:11.460171 getpriority(PRIO_PROCESS, 0) = 20
06:38:11.464099 ioctl(7, 0xc0186201, 0x103ffda8) = 0
06:38:11.478595 ioctl(7, 0xc0186201
This thread doesn't do much, it seems similar to the previous, where it is simply sitting waiting for IPC messages and adding services.
06:38:07.446832 syscall_983045(0x104fff80, 0, 0x20, 0, 0x104fff80, 0x140d8, 0xa9d25ef9, 0xf0005, 0x103ffd90, 0xafe36e70, 0xa9d25ef9, 0x1, 0, 0x104fff68, 0xafe0b238, 0xafe0949c, 0x60000010, 0x104fff80, 0xc13c0, 0, 0xc764, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) = 0
06:38:07.450234 getpid() = 479
06:38:07.452799 getuid32() = 0
06:38:07.471704 ioctl(7, 0xc0186201, 0x104ffef8) = 0
06:38:11.449551 writev(4, [{"\4", 1}, {"ServiceManager\0", 15}, {"ServiceManager: addService(org.bluez.bluetooth_mailbox, 0x15db0)\n\0", 66}], 3) = 82
06:38:11.453639 futex(0x134ac, FUTEX_WAKE, 2147483647) = 0
06:38:11.454617 getpriority(PRIO_PROCESS, 0) = 20
06:38:11.460965 ioctl(7, 0xc0186201, 0x104ffda8) = 0
06:38:11.470727 ioctl(7, 0xc0186201
So there it is. A fairly detailed breakdown of what the
runtime process does. Next up is to find out what
zygote does. It looks like it is basically something like
a CORBA ORB. It would definitely be nice to understand a lot more
about the IPC.
In this post I want to try and find out more stuff about the Android software framework initialises. I previously looked a little bit at the boot process. Now I really want to get a better idea of what is actually happening with the system runtime.
The important processes in the system appear to be zygote
(which is the app_process binary), and runtime.
These are both started up by the init process. The following
fragment of init.rc is relevant. It is important to note that
these services have the autostart 1 attribute, indicating they
are restarted if they quit.
zygote {
exec /system/bin/app_process
args {
0 -Xzygote
1 /system/bin
2 --zygote
}
autostart 1
}
runtime {
exec /system/bin/runtime
autostart 1
}
If we go and kill either the zygote, or
runtime process something interesting happens. Firstly
the other process seems to kill itself, and then the whole system
seems to restart.
I want to get a bit more control of what things are starting up when.
To do this I need to modify the init.rc file. To do this
I first extracted the the ramdisk to the fileystem so that I can modify
it (gnucpio -iz -F ramdisk.img).
After this I simply commented out the line from init.rc. Then
we can recreate it: (gnucpio -i -t -F ../ramdisk.img | gnucpio -o -H newc -O ../rootfs.img).
Note if you are using Mac OS X, you will need to download and install
gnucpio, becuase the built-in cpio doesn't support newc.
We can then use the emulator -ramdisk option to load
our new ramdisk.
Now we can start things up manually. First we start up the zygote process.
As # app_process -Xzygote /system/bin --zygote. When we do that there
is no output either on the console, or on the LCD. Next we start the runtime process
( # runtime), and now things start to happen!
Once both processes start we get the cylon image, and we also end up with some console output:
Prepping: /system/app/AlarmProvider.apk:/system/app/Browser.apk:/system/app/Ca lendar.apk:/system/app/Camera.apk:/system/app/Contacts.apk:/system/app/Developm ent.apk:/system/app/GDataFeedsProvider.apk:/system/app/Gmail.apk:/system/app/Gm ailProvider.apk:/system/app/GoogleApps.apk:/system/app/GoogleAppsProvider.apk:/ system/app/Home.apk:/system/app/ImProvider.apk:/system/app/Maps.apk:/system/app /MediaPickerActivity.apk:/system/app/MediaProvider.apk:/system/app/Phone.apk:/s ystem/app/PimProvider.apk:/system/app/ApiDemos.apk:/system/app/SettingsProvider .apk:/system/app/Sms.apk:/system/app/SyncProvider.apk:/system/app/TelephonyProv ider.apk:/system/app/XmppService.apk:/system/app/YouTube.apk File not found: /system/app/AlarmProvider.apk File not found: /system/app/Calendar.apk File not found: /system/app/Camera.apk File not found: /system/app/GDataFeedsProvider.apk File not found: /system/app/Gmail.apk File not found: /system/app/GmailProvider.apk File not found: /system/app/MediaPickerActivity.apk File not found: /system/app/PimProvider.apk File not found: /system/app/ApiDemos.apk File not found: /system/app/Sms.apk File not found: /system/app/SyncProvider.apk File not found: /system/app/YouTube.apk Prep complete
It might give some clue of which Google applications will be available in the future. :)
The output from runtime looks like this:
+++ post-zygote
There is also some interesting output from the kernel. In
particular the binder_open log. We can look at these in
more detail later.
binder_open(c086f300 c0832160) (pid 462) got c5cea000 binder_open(c086f300 c095ec40) (pid 475) got c4e8b000 binder_open(c086f300 c095ef40) (pid 476) got c46f8000 android_power: auto off timeout set to 604800 seconds Descriptor2Node failed secondary: desc=4, max=32, node=c4e8c350, weak=0 binder_open(c086f300 c4008d60) (pid 513) got c473a000 binder_open(c086f300 c4008760) (pid 512) got c39fc000 binder_open(c086f300 c4008260) (pid 531) got c35e3000
That is about enough detail for this entry, next time I'll be using
strace to try and get a bit more information about these
processes.
So, I'm intrigued as to what exactly happens when you install a package.
The way to work this out is get a look at the /data filesystem
before package installation, then again afterwards, and do a big recursive
diff
So lets get a relatively clean image:
emulator -wipe-dataadb push busybox ./adb shell ./busybox tar c -f /tmp/data.tar /dataadb pull /tmp/data.tar .mkdir originalcd originaltar xf ../data.tarNow that we have the clean image. Lets compile the simple Hello World sample.
activityCreator.py --out HelloAndroid com.google.android.hello.HelloAndroidcd HelloAndroidantNow we have HelloAndroid.apk. So we should install this, and then
we can find the diff of what happened.
adb install HelloAndroid/bin/HelloAndroid.apkadb shell ./busybox tar c -f /tmp/data.tar /dataadb pull /tmp/data.tar .mkdir after_installcd after_installtar xf ../data.tarWhen we diff this we find not much as happened. There is
the new HelloAndroid.apk file installed in the data/app
directory. There is also a new com.google.android.hello in
data/data, but it is empty, so not that interesting.
Only in after_install/data/app: HelloAndroid.apk Only in after_install/data/data: com.google.android.hello diff -ur original/data/system/packages.xml after_install/data/system/packages.xml --- original/data/system/packages.xml 2007-11-17 14:27:17.000000000 +1100 +++ after_install/data/system/packages.xml 2007-11-17 14:34:20.000000000 +1100 @@ -5,6 +5,7 @@ <package name="com.google.android.home" userId="10004" /> <package name="com.google.android.fallback" userId="10003" /> <package name="com.google.android.contacts" userId="10001" /> + <package name="com.google.android.hello" userId="10006" /> <shared-user name="android.uid.system" dummy-signature="android.signature.system" userId="1000" /> <shared-user name="android.uid.phone" dummy-signature="android.signature.system" userId="1001" /> <shared-user name="com.google.android.core" dummy-signature="com.google" userId="10002" />
This is pretty interesting to me. I'd really like to know how it
finds the thing in the menu. As an experiment I'm going to edit the
packages.xml file to see if this removes it from the menu.
adb push original/data/system/packages.xml /data/system/packages.xml does
this easily. Changing this doesn't update the menu immediately. On reboot it seems
that the packages file is regenerated. Not much luck here, time to get a better idea
of what is in the .apk file.
So, it turns out the .apk file isn't that difficult. file HelloAndroid.apk,
tells us it is just a zip file. After extracting the zip file, we see just 4 files:
The XML files would presumably the most obvious, but they don't
seem to be textual. file suggests it is a DBase 3 file,
but that doesn't seem so likely. My guess is some kind of either
unicode, or binary XML format, or actually both. I can't actually work
it out to be perfectly honest! So, some reverse engineering required. The
strings definitely look like UTF-16. hexdump helps to debug
what is going on. The first part of the file looks like:
$ cat AndroidManifest.xml | hexdump -C 00000000 03 00 08 00 dc 05 00 00 01 00 1c 00 88 02 00 00 |................| 00000010 13 00 00 00 00 00 00 00 01 00 00 00 68 00 00 00 |............h...| 00000020 00 00 00 00 6a 00 00 00 c8 00 00 00 0a 01 00 00 |....j...........| 00000030 36 01 00 00 70 01 00 00 e8 00 00 00 00 00 00 00 |6...p...........| 00000040 8e 01 00 00 da 01 00 00 ce 00 00 00 c6 01 00 00 |................| 00000050 fc 00 00 00 94 00 00 00 12 00 00 00 52 01 00 00 |............R...| 00000060 28 01 00 00 6e 00 00 00 82 00 00 00 80 01 00 00 |(...n...........| 00000070 07 00 61 00 6e 00 64 00 72 00 6f 00 69 00 64 00 |..a.n.d.r.o.i.d.| 00000080 00 00 2a 00 68 00 74 00 74 00 70 00 3a 00 2f 00 |..*.h.t.t.p.:./.| 00000090 2f 00 73 00 63 00 68 00 65 00 6d 00 61 00 73 00 |/.s.c.h.e.m.a.s.| 000000a0 2e 00 61 00 6e 00 64 00 72 00 6f 00 69 00 64 00 |..a.n.d.r.o.i.d.| 000000b0 2e 00 63 00 6f 00 6d 00 2f 00 61 00 70 00 6b 00 |..c.o.m./.a.p.k.| 000000c0 2f 00 72 00 65 00 73 00 2f 00 61 00 6e 00 64 00 |/.r.e.s./.a.n.d.| 000000d0 72 00 6f 00 69 00 64 00 00 00 00 00 00 00 08 00 |r.o.i.d.........| 000000e0 6d 00 61 00 6e 00 69 00 66 00 65 00 73 00 74 00 |m.a.n.i.f.e.s.t.| 000000f0 00 00 07 00 70 00 61 00 63 00 6b 00 61 00 67 00 |....p.a.c.k.a.g.| 00000100 65 00 00 00 18 00 63 00 6f 00 6d 00 2e 00 67 00 |e.....c.o.m...g.| 00000110 6f 00 6f 00 67 00 6c 00 65 00 2e 00 61 00 6e 00 |o.o.g.l.e...a.n.| 00000120 64 00 72 00 6f 00 69 00 64 00 2e 00 68 00 65 00 |d.r.o.i.d...h.e.| 00000130 6c 00 6c 00 6f 00 00 00 01 00 20 00 00 00 0b 00 |l.l.o..... .....| 00000140 61 00 70 00 70 00 6c 00 69 00 63 00 61 00 74 00 |a.p.p.l.i.c.a.t.| 00000150 69 00 6f 00 6e 00 00 00 08 00 61 00 63 00 74 00 |i.o.n.....a.c.t.| 00000160 69 00 76 00 69 00 74 00 79 00 00 00 05 00 63 00 |i.v.i.t.y.....c.| 00000170 6c 00 61 00 73 00 73 00 00 00 0d 00 2e 00 48 00 |l.a.s.s.......H.| 00000180 65 00 6c 00 6c 00 6f 00 41 00 6e 00 64 00 72 00 |e.l.l.o.A.n.d.r.| 00000190 6f 00 69 00 64 00 00 00 05 00 6c 00 61 00 62 00 |o.i.d.....l.a.b.| 000001a0 65 00 6c 00 00 00 0c 00 48 00 65 00 6c 00 6c 00 |e.l.....H.e.l.l.| 000001b0 6f 00 41 00 6e 00 64 00 72 00 6f 00 69 00 64 00 |o.A.n.d.r.o.i.d.| 000001c0 00 00 0d 00 69 00 6e 00 74 00 65 00 6e 00 74 00 |....i.n.t.e.n.t.| 000001d0 2d 00 66 00 69 00 6c 00 74 00 65 00 72 00 00 00 |-.f.i.l.t.e.r...| 000001e0 06 00 61 00 63 00 74 00 69 00 6f 00 6e 00 00 00 |..a.c.t.i.o.n...| 000001f0 05 00 76 00 61 00 6c 00 75 00 65 00 00 00 1a 00 |..v.a.l.u.e.....| 00000200 61 00 6e 00 64 00 72 00 6f 00 69 00 64 00 2e 00 |a.n.d.r.o.i.d...| 00000210 69 00 6e 00 74 00 65 00 6e 00 74 00 2e 00 61 00 |i.n.t.e.n.t...a.| 00000220 63 00 74 00 69 00 6f 00 6e 00 2e 00 4d 00 41 00 |c.t.i.o.n...M.A.| 00000230 49 00 4e 00 00 00 08 00 63 00 61 00 74 00 65 00 |I.N.....c.a.t.e.| 00000240 67 00 6f 00 72 00 79 00 00 00 20 00 61 00 6e 00 |g.o.r.y... .a.n.| 00000250 64 00 72 00 6f 00 69 00 64 00 2e 00 69 00 6e 00 |d.r.o.i.d...i.n.| 00000260 74 00 65 00 6e 00 74 00 2e 00 63 00 61 00 74 00 |t.e.n.t...c.a.t.| 00000270 65 00 67 00 6f 00 72 00 79 00 2e 00 4c 00 41 00 |e.g.o.r.y...L.A.| 00000280 55 00 4e 00 43 00 48 00 45 00 52 00 00 00 00 00 |U.N.C.H.E.R.....| 00000290 80 01 08 00 54 00 00 00 00 00 00 00 00 00 00 00 |....T...........|
Well, this hasn't exactly been very informative. We've learned that
basically all that happens on install is the .apk file is
copied to /data/app. We also learned that this directory
is scanned on startup to find packages to start. The strings in there are:
We can note that there aren't any duplicated strings, and updating
the AndroidManifest.xml source file and regenerating it
confirms this.
So looking at the WbXML looks likely. The first byte is '3', which indicates WbXML version 1.3. The next byte is '0', which indicates that public identifier is described by a string, which string index number '8'. The charset is indicated by '0', which means unknown. Next is the string table, and it looks bytes 'dc' and '05', which, if I've decode it right, indicates 11781 bytes. Which makes no sense at all. So we try and guess something else. As a 32-bit little-endian integer 0x5dc is exactly the length of the file. So that seems like a good guess for what that field is. In this case, I'm going to guess that '03 00 08 00', is a magic number to identify the file. The next 4 bytes are '01 00 1c 00', not sure hat this is it is followed by '88 02 00 00', which happen to be 0x288 little endian, which seem to represent the last character in what looks like a string table. Looking at other different binary XML files seems to confirm this hypothesis. The next field appears to be 0x13, which is pretty close to the number of strings we found earlier. And this is about as far as I can be bothered working out right now.
The use of the binary encoding seems a little strange. It certainly doesn't reduce the size, but maybe that isn't really the point. It is probably the case that this makes it a lot easier to parse, but you would expect there to be existing XML parsers in the libraries. So beats me what is going on!
The classes.dex file include in the .apk file is already documented by Retrodev.
The final file is the resources.arsc file. This
doesn't seem to have too much information in it. There are a few
strings that we would expect including,
res/layout/main.xml, HelloAndroid and
com.google.android.hello.
And that is about it for now. Not so useful at the end of the day, but it maybe give you some information as starting point for further reverse engineering.
The toolbox that is provided on the Android environment is pretty limited. I wanted something more useful and familiar. Busybox to the rescue!
I have a busybox binary available for those who are interested.
To use it simply do # mkdir /data/busybox, on your
emulated console. Then copy the busybox binary across: $ adb
push busybox /data/busybox/busybox. Once you do this you can
install the necessary hardlinks by doing: # cd /data/busybox;
./busybox --install. Once doing this you should have links to
all the applets in the /data/busybox directory. To
make this useful you probably want to put this into your PATH:
# export PATH=/data/busybox:$PATH.
You should now be able to get access to all the useful busybox applets.
Update:busybox was a straight compile of the standard busybox 1.8.1 release. No changes were made to the source. Source code available here or from the Busybox website.
For those of you hacking on Android, you might find it interesting to have the actual files extracted so that you can play with them. Here are the tarballs of the images I extracted from my running system. system.tar.gz data.tar.gz. Note these were extracted from a running system, they will have some cruft on them.
The Android Java SDK is nice and all, but what if you want to run some C service or code? Well, it turns out that this isn't exactly difficult. You can compile an application using a standard Linux cross-compiler, install it and run your programs from the shell.
You will need a cross-compiler (make sure you get the ARM GNU/Linux target). Then you can just create your program, compile, and upload to the device:
$ arm-none-linux-gnueabi-gcc -static hello.c -o hello
$ adb push hello/hello data/hello
You can then simply run you application:
$ adb shell data/hello Hello, Android!
Now of course, this doesn't at all explain how to tie into the graphics or the rest of the system, but that is left as an exercise for the reader ;)
Update: My instructions were missing the essential
-static part!
I'm really interested in finding out what is happening underneath
the sheets on the Android platform. The first step to this is getting
access to the Android shell. There are two options here, you can either
start the emulator with a console option: $ emulator console,
or connect to the console after book using: $ adb shell. A convention
that I use through this document is that thing to type into you host shell use
the '$' prompt, and things to type into the Android console use '#'.
Apology: This information isn't particularly well structured, and has mostly been written as a log of my initial explorations. At some time in the future I might actually order this in a sane way.
So our toolbox of useful POSIX commands is a little more limited than
usual, but the basics of ls, ps, cat, echo are there. So
we start by seeing what programs we can run. First we do an # echo $PATH,
which shows we can expect useful programs in /sbin, /system/sbin and
/system/bin/. So lets look a little closer. # ls /sbin, shows us:
-rwxr-xr-x root root 226940 1970-01-01 00:00 recovery -rwxr-xr-x root root 102864 1970-01-01 00:00 adbd
recovery seems to be some kind of recovery tool for
when you are screwed things up a bit too much. Running recovery from
the build directory seems to actual output text on the LCD screen,
specifically: W: Can't open version file "/etc/build.prop": No
such file or directory. There isn't a build.prop
file in /etc, but there is one in /system,
it would be interesting to see what happens when recovery does find a
file. Unfortunately, we don't actually have cp, but we
can use adb, to do our dirty work: $ adb pull
/system/build.prop .; adb push build.prop /etc. Running
recovery now doesn't actually do anything very useful, so much for that.
I presume that it is meant to be run by some process on the host, so
we'll ignore this avenue of enquiry for now.
adbd, seems pretty obvious, it looks like the daemon
that adb talks to. If we run # ps, we can see
that the adbd process is running.
It turns out there is no /system/sbin directory, so we
are left looking at what is in /system/bin, which seems
to be where the bulk of the files are, around 100 executables.
-rwxr-xr-x root root 4280 2007-11-11 20:57 AudioHardwareRecord -rwxr-xr-x root root 4152 2007-11-11 20:57 AudioInRecord -rwxr-xr-x root root 4672 2007-11-11 20:57 RecursiveMutexTest -rwxr-xr-x root root 20932 2007-11-11 20:57 SRecTest -rwxr-xr-x root root 20408 2007-11-11 20:57 SRecTestAudio -rwxr-xr-x root root 4860 2007-11-11 20:57 UAPI_PortabilityTest -rwxr-xr-x root root 12420 2007-11-11 20:57 UAPI_SrecTest -rwxr-xr-x root root 29560 2007-11-11 20:57 UAPI_test -rwxr-xr-x root root 196 2007-11-11 20:46 am -rwxr-xr-x root root 5124 2007-11-11 20:57 app_process lrwxr-xr-x root root 2007-11-11 20:57 cat -> toolbox lrwxr-xr-x root root 2007-11-11 20:57 chmod -> toolbox lrwxr-xr-x root root 2007-11-11 20:57 cmp -> toolbox -rwxr-xr-x root root 3912 2007-11-11 20:57 crasher -rwxr-xr-x root root 4892 2007-11-11 20:57 dalvikvm lrwxr-xr-x root root 2007-11-11 20:57 date -> toolbox -rwxr-xr-x root root 118096 2007-11-11 20:57 dbus-daemon lrwxr-xr-x root root 2007-11-11 20:57 dd -> toolbox -rwxr-xr-x root root 9264 2007-11-11 20:57 debuggerd -rwxr-xr-x root root 17416 2007-11-11 20:57 dexdump -rwxr-xr-x root root 3944 2007-11-11 20:57 dexopt lrwxr-xr-x root root 2007-11-11 20:57 df -> toolbox lrwxr-xr-x root root 2007-11-11 20:57 dmesg -> toolbox -rwxr-xr-x root root 73748 2007-11-11 20:57 drm1_unit_test -rwxr-xr-x root root 85608 2007-11-11 20:57 drm2_unit_test -rwxr-xr-x root root 1240 2007-11-11 20:46 dumpstate -rwxr-xr-x root root 6808 2007-11-11 20:57 dumpsys lrwxr-xr-x root root 2007-11-11 20:57 exists -> toolbox lrwxr-xr-x root root 2007-11-11 20:57 getevent -> toolbox lrwxr-xr-x root root 2007-11-11 20:57 getprop -> toolbox lrwxr-xr-x root root 2007-11-11 20:57 hd -> toolbox lrwxr-xr-x root root 2007-11-11 20:57 ifconfig -> toolbox -rwxr-xr-x root root 208 2007-11-11 20:46 input lrwxr-xr-x root root 2007-11-11 20:57 insmod -> toolbox -rwxr-xr-x root root 6696 2007-11-11 20:57 install_boot_image lrwxr-xr-x root root 2007-11-11 20:57 ioctl -> toolbox lrwxr-xr-x root root 2007-11-11 20:57 kill -> toolbox -rwxr-xr-x root root 81236 2007-11-11 20:57 linker lrwxr-xr-x root root 2007-11-11 20:57 ln -> toolbox lrwxr-xr-x root root 2007-11-11 20:57 log -> toolbox -rwxr-xr-x root root 8772 2007-11-11 20:57 logcat lrwxr-xr-x root root 2007-11-11 20:57 ls -> toolbox lrwxr-xr-x root root 2007-11-11 20:57 lsmod -> toolbox -rwxr-xr-x root root 7596 2007-11-11 20:57 mem_profiler lrwxr-xr-x root root 2007-11-11 20:57 mkdir -> toolbox lrwxr-xr-x root root 2007-11-11 20:57 mkdosfs -> toolbox -rwxr-xr-x root root 212 2007-11-11 20:46 monkey lrwxr-xr-x root root 2007-11-11 20:57 mount -> toolbox -rwxr-xr-x root root 2888 2007-11-11 20:57 mtptest -rwxr-xr-x root root 8640 2007-11-11 20:57 netcfg lrwxr-xr-x root root 2007-11-11 20:57 netstat -> toolbox lrwxr-xr-x root root 2007-11-11 20:57 notify -> toolbox lrwxr-xr-x root root 2007-11-11 20:57 ping -> toolbox lrwxr-xr-x root root 2007-11-11 20:57 powerd -> toolbox -rwxr-xr-x root root 144356 2007-11-11 20:57 pppd lrwxr-xr-x root root 2007-11-11 20:57 printenv -> toolbox lrwxr-xr-x root root 2007-11-11 20:57 ps -> toolbox -rwxr-xr-x root root 8724 2007-11-11 20:58 pv lrwxr-xr-x root root 2007-11-11 20:57 r -> toolbox lrwxr-xr-x root root 2007-11-11 20:57 readtty -> toolbox lrwxr-xr-x root root 2007-11-11 20:57 reboot -> toolbox lrwxr-xr-x root root 2007-11-11 20:57 renice -> toolbox lrwxr-xr-x root root 2007-11-11 20:57 resetradio -> toolbox -rwxr-xr-x root root 4116 2007-11-11 20:57 rild lrwxr-xr-x root root 2007-11-11 20:57 rm -> toolbox lrwxr-xr-x root root 2007-11-11 20:57 rmdir -> toolbox lrwxr-xr-x root root 2007-11-11 20:57 rmmod -> toolbox -rwxr-xr-x root root 1027 2007-11-11 20:47 ro.xml -rwxr-xr-x root root 1782 2007-11-11 20:47 ro2.xml -rwxr-xr-x root root 98 2007-11-11 20:47 roerror.xml lrwxr-xr-x root root 2007-11-11 20:57 rotatefb -> toolbox lrwxr-xr-x root root 2007-11-11 20:57 route -> toolbox -rwxr-xr-x root root 45484 2007-11-11 20:57 runtime -rwxr-xr-x root root 4572 2007-11-11 20:57 sdutil lrwxr-xr-x root root 2007-11-11 20:57 sendevent -> toolbox -rwxr-xr-x root root 6148 2007-11-11 20:57 service lrwxr-xr-x root root 2007-11-11 20:57 setconsole -> toolbox lrwxr-xr-x root root 2007-11-11 20:57 setkey -> toolbox lrwxr-xr-x root root 2007-11-11 20:57 setprop -> toolbox -rwxr-xr-x root root 90344 2007-11-11 20:57 sh -rwxr-xr-x root root 5116 2007-11-11 20:57 showmap -rwxr-xr-x root root 7524 2007-11-11 20:57 showslab lrwxr-xr-x root root 2007-11-11 20:57 sleep -> toolbox lrwxr-xr-x root root 2007-11-11 20:57 smd -> toolbox -rwxr-xr-x root root 25944 2007-11-11 20:57 sqlite3 -rwxr-xr-x root root 411 2007-11-11 20:47 ssltest lrwxr-xr-x root root 2007-11-11 20:57 start -> toolbox lrwxr-xr-x root root 2007-11-11 20:57 stop -> toolbox -rwsr-sr-x root root 72436 2007-11-11 20:57 su lrwxr-xr-x root root 2007-11-11 20:57 sync -> toolbox lrwxr-xr-x root root 2007-11-11 20:57 syren -> toolbox -rwxr-xr-x root root 2772 2007-11-11 20:57 system_server -rwxr-xr-x root root 88052 2007-11-11 20:57 toolbox lrwxr-xr-x root root 2007-11-11 20:57 umount -> toolbox -rwxr-xr-x root root 16612 2007-11-11 20:57 usbd lrwxr-xr-x root root 2007-11-11 20:57 watchprops -> toolbox lrwxr-xr-x root root 2007-11-11 20:57 webgrab -> toolbox lrwxr-xr-x root root 2007-11-11 20:57 wipe -> toolbox
We can use # mount to get an idea of what is happening.
rootfs / rootfs rw 0 0 /dev/pts /dev/pts devpts rw 0 0 /proc /proc proc rw 0 0 /sys /sys sysfs rw 0 0 /dev/block/mtdblock0 /system yaffs2 rw,nodev,noatime,nodiratime 0 0 /dev/block/mtdblock1 /data yaffs2 rw,nodev,noatime,nodiratime 0 0
The root filesystem is
using rootfs,
which basically means it is whatever what in the initial-ramfs. It is
interesting, because there is no tmpfs, which means an
application writing to /tmp, could exhaust system
memory. This doesn't seem like a good denial-of-service to leave
open. Of course the root-filesystem is no good for storing files,
since it has no backing store.
The initial-ramfs is loaded by the emulator
from $SDKROOT/tools/lib/images/ramdisk.img. This is
simply a gzipped, CPIO archive. So, if you wanted to modify it, for
some reason, it wouldn't be too hard to do. Inspecting it there isn't
too much interesting there though, most of the interesting stuff is on
the /system and /data partitions.
/system is
a yaffs2 filesystem. It is using
the first nand chip as its backing store. # df shows us
that there is a 64MB backing store, and it is about half used. This
directory is where the core of the Android system is stored. It seems
as though a normal user shouldn't ever need to update it, but I'm sure
over-the-air updates would be used on this file system.
In the emulator the system image is located
at $SDKROOT/tools/lib/image/system.img, although you should
be able to change that by using the -image parameter when
running the emulator. The emulator does not write-back changes to the system
file-system, so it will be fresh, each time the emulator is loaded.
The /data file system is very similar to /system;
it is backed by 64MB nand, and is using the yaffs2. The is the filesystem on which
new user applications are installed and run from. Changes made to the /data
filesystem are honoured by the emulator, the updated nand image is stored in ~/.android/userdata.img.
To get an idea of what is actually executing on the phone we use
trusty # ps. Which gives us:
USER PID PPID VSIZE RSS WCHAN PC NAME root 1 0 252 164 c0082240 0000ab0c S /init root 2 0 0 0 c0048eac 00000000 S kthreadd root 3 2 0 0 c003acf0 00000000 S ksoftirqd/0 root 4 2 0 0 c0045e5c 00000000 S events/0 root 5 2 0 0 c0045e5c 00000000 S khelper root 8 2 0 0 c0045e5c 00000000 S suspend/0 root 33 2 0 0 c0045e5c 00000000 S kblockd/0 root 36 2 0 0 c0045e5c 00000000 S cqueue/0 root 38 2 0 0 c0150c44 00000000 S kseriod root 75 2 0 0 c005bed0 00000000 S pdflush root 76 2 0 0 c005bed0 00000000 S pdflush root 77 2 0 0 c005f880 00000000 S kswapd0 root 78 2 0 0 c0045e5c 00000000 S aio/0 root 201 2 0 0 c014e2f4 00000000 S mtdblockd root 217 2 0 0 c0045e5c 00000000 S kmmcd root 231 2 0 0 c0045e5c 00000000 S rpciod/0 root 450 1 720 288 c00386a4 afe092ac S /system/bin/sh root 451 1 3316 128 ffffffff 0000ceb4 S /sbin/adbd root 452 1 2816 284 ffffffff afe08b9c S /system/bin/usbd root 453 1 636 216 c017c114 afe08e9c S /system/bin/debuggerd root 454 1 12576 584 ffffffff afe08b9c S /system/bin/rild root 455 1 56572 14608 c01dc388 afe083dc S zygote root 456 1 20576 2076 ffffffff afe0861c S /system/bin/runtime bluetooth 458 1 1200 760 c0082240 afe0947c S /system/bin/dbus-daemon root 467 455 93632 17284 ffffffff afe0861c S system_server app_4 505 455 76104 13632 ffffffff afe09604 S com.google.android.home phone 509 455 75876 14952 ffffffff afe09604 S com.google.android.phone app_2 523 455 74528 14332 ffffffff afe09604 S com.google.process.content root 538 450 932 312 00000000 afe083dc R ps
Unfortunately we don't have pstree, so we don't get a
nice hierarchal display, but things aren't very complicated so we can
work it out fairly easily. First we can prune off the kernel threads,
and we get a more manageable set of things left. If we then look at
those processes directly parented by init, we are left
with:
USER PID PPID VSIZE RSS WCHAN PC NAME root 450 1 720 288 c00386a4 afe092ac S /system/bin/sh root 451 1 3316 128 ffffffff 0000ceb4 S /sbin/adbd root 452 1 2816 284 ffffffff afe08b9c S /system/bin/usbd root 453 1 636 216 c017c114 afe08e9c S /system/bin/debuggerd root 454 1 12576 584 ffffffff afe08b9c S /system/bin/rild root 455 1 56572 14608 c01dc388 afe083dc S zygote root 456 1 20576 2076 ffffffff afe0861c S /system/bin/runtime bluetooth 458 1 1200 760 c0082240 afe0947c S /system/bin/dbus-daemon
/system/bin/sh, is clearly the shell we are using. We have already
explored adbd earlier. usbd, is an interesting. It seems
that most of the interaction between the client debugger happens over the USB port,
and is handled by the USB daemon. There is a usbd.conf file in
/etc. I'm not quite sure how this is working though, as there does not
appear to be any USB devices in the system (# cat /proc/devices).
debuggerd, is presumably the client side of the system debugger, although
I can't find out more details than that. rild is the radio-interface-link
daemon (I think! ;). The init files imply that you can connect a real-modem and
get Android to us it, but by default it has the simulated Java radio-interface.
zygote appears to get the launcher for the Java runtime, it isn't an
actual program, it looks like app_process, masquerading as zygote.
User-land starts with the init program. This doesn't appear to be a
standard init by any shot. At startup init parses /etc/init.rc,
which has the overall format of:
## Global environment setup
##
env {
PATH /sbin:/system/sbin:/system/bin
LD_LIBRARY_PATH /system/lib
ANDROID_BOOTLOGO 1
ANDROID_ROOT /system
ANDROID_ASSETS /system/app
ANDROID_DATA /EXTERNAL
data_STORAGE /sdcard
DRM_CONTENT /data/drm/content
}
## Setup and initialization we need to do on boot
##
onboot {
setkey 0x0 0xe5 0x706
....
# bring up loopback
ifup lo
...
}
## Processes which are run once (and only once) at system boot.
## Init will wait until each of these completes before starting
## the next one.
##
startup {
usbd-config {
exec /system/bin/usbd
args {
0 -c
}
}
qemu-init {
exec /etc/qemu-init.sh
}
}
## Processes which init runs and will re-run upon exit,
## until it is told to stop running them. These will not be
## run until all the processes in startup have been run
##
service {
console {
exec /system/bin/sh
console yes
}
adbd {
exec /sbin/adbd
}
......
}
I've no idea of a general purpose init that uses that file format,
but really, I don't know that much. It looks custom, although seems to
have familiar constructs to something like
Apple's launchd. strings doesn't provide
much more information, but it seems someone was frustrated enough
during development to see the need for the string Stupid C library hack !!.
Well, that is a bit of an overview. I'm sure there will be more to come as I work out more stuff.
Last night Open Kernel Labs won an iAward in the Application and Infrastructure Tools category.
It is quite cool that I can now say our microkernel is now award winning! I think a lot of this is really due the awesome engineers working here.
Running with the explain things for Benno 5 months from now, here is how I'm currently making my backups work. The basic thing I want is incremental backups onto an external hard drive.
rsync makes this really
easy to do. The latest version of rsync has --link-dest,
which will hardlink to files in an existing directory tree, rather than
creating a new file. This makes each incremental reasonably cheap, as
most files are just hard links back to the previous backup.
So basically I do:
sudo rsync -a --link-dest=../backups.0/ /Users/ backups.1/.
Note that the trailing slash is actually important here.
A big gotcha is ensuring that the external disk is actually mounted with permissions enabled, or else the hard-links won't actual be made.
After this everything works nicely, when combined with a simple script:
#!/usr/bin/env python
"""
Use rsync to backup my /Users directory onto external hard drive
backup.
"""
import os
def get_latest():
backups = [int(x.split(".")[-1]) for x in os.listdir(".") if x.startswith("backup")]
backups.sort()
return backups[-1]
os.chdir("/Volumes/backup2007")
latest = get_latest()
next = latest + 1
command = "sudo rsync -a --link-dest=../backups.%d/ /Users/ backups.%d/" % (latest, next)
os.system(command)
Currently I don't do any encryption, which might be useful in the future.