busybox and upx on arm

Denys Vlasenko vda.linux at googlemail.com
Sun Jul 19 14:13:30 UTC 2009


On Sunday 19 July 2009 13:12, Tito wrote:
> On Thursday 09 July 2009 23:21:06 Denys Vlasenko wrote:
> > On Tuesday 07 July 2009 21:45, Tito wrote:
> > > Hi,
> > > I'm experiencing a strange behaviour of busybox.
> > > I'm trying to use upx on an android g1 phone.
> > > So far I was able to cross-compile a statically linked 
> > > and working copy of upx
> > > upx compresses the executables on the phone just fine,
> > > only busybox seems not to work as expected.
> > > upx compresses it but when the compressed
> > > busybox is launched it runs the command as
> > > expected but never returns to the shell unless it is killed.
> > > The only suspect log i can see after killing it is:
> > > 
> > > <3>[ 3261.966003] init: untracked pid 5064 exited
> > > <3>[ 3290.064056] init: untracked pid 5066 exited
> > > <3>[ 3339.908325] init: untracked pid 5069 exited
> > > 
> > > Any hints about what is going wrong here?
> > > 
> > > BTW: busybox packed with upx on x86 works.
> > > 
> > > /system/sd # ./upx busybox
> > >                        Ultimate Packer for eXecutables
> > >                           Copyright (C) 1996 - 2009
> > > UPX 3.04        Markus Oberhumer, Laszlo Molnar & John Reiser   Apr 27th 2009
> > > 
> > >         File size         Ratio      Format      Name
> > >    --------------------   ------   -----------   -----------
> > >     690276 ->    381428   55.26%   linux/armel   busybox
> > > 
> > > Packed 1 file.
> > > /system/sd # ./busybox
> > > BusyBox v1.14.2 (2009-07-01 18:27:27 EDT) multi-call binary
> > > Copyright (C) 1998-2008 Erik Andersen, Rob Landley, Denys Vlasenko
> > > and others. Licensed under GPLv2.
> > > See source distribution for full notice.
> > > 
> > > Usage: busybox [function] [arguments]...
> > >    or: function [arguments]...
> > > 
> > >         BusyBox is a multi-call binary that combines many common Unix
> > >         utilities into a single executable.  Most people will create a
> > >         link to busybox for each function they wish to use and BusyBox
> > >         will act like whatever it was invoked as!
> > > 
> > > Currently defined functions:
> > >         [, [[, arping, ash, awk, basename, bbconfig, bunzip2, bzcat,
> > >         bzip2, cat, catv, chgrp, chmod, chown, chroot, chrt, chvt, cksum,
> > >         clear, cmp, cp, cpio, crond, crontab, cut, date, dc, dd, deallocvt,
> > >         depmod, devmem, df, dhcprelay, diff, dirname, dmesg, dnsd, dos2unix,
> > >         du, dumpkmap, dumpleases, echo, egrep, env, ether-wake, expr,
> > >         false, fbset, fdisk, fgrep, find, fold, free, freeramdisk, fuser,
> > >         getopt, grep, gunzip, gzip, head, hexdump, hostname, hwclock,
> > >         ifconfig, ifdown, ifup, insmod, install, ip, ipaddr, iplink,
> > >         iproute, iprule, iptunnel, kbd_mode, kill, killall, killall5,
> > >         last, length, less, ln, loadfont, loadkmap, losetup, ls, lsmod,
> > >         makedevs, md5sum, mdev, mkdir, mkfifo, mknod, mkswap, mktemp,
> > >         modprobe, more, mount, mountpoint, mv, nameif, nc, netstat,
> > >         nice, nohup, nslookup, od, openvt, patch, pidof, ping, pipe_progress,
> > >         pivot_root, printenv, printf, ps, pwd, rdate, rdev, readlink,
> > >         readprofile, realpath, renice, reset, rm, rmdir, rmmod, route,
> > >         run-parts, sed, seq, setconsole, setkeycodes, setlogcons, setsid,
> > >         sh, sha1sum, showkey, sleep, sort, split, stat, strings, stty,
> > >         swapoff, swapon, switch_root, sync, sysctl, tac, tail, tar,
> > >         tcpsvd, tee, telnet, telnetd, test, tftp, time, top, touch,
> > >         tr, traceroute, true, tty, udhcpd, udpsvd, umount, uname, uniq,
> > >         unix2dos, unzip, uptime, usleep, uudecode, uuencode, vconfig,
> > >         vi, watch, wc, wget, which, who, whoami, xargs, yes, zcat
> > > Killed
> > 
> > Try in libbb/appletlib.c:
> > 
> > 
> > void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv)
> > {
> >         int argc = 1;
> > 
> >         while (argv[argc])
> >                 argc++;
> > 
> >         /* Reinit some shared global data */
> >         xfunc_error_retval = EXIT_FAILURE;
> > 
> >         applet_name = APPLET_NAME(applet_no);
> >         if (argc == 2 && strcmp(argv[1], "--help") == 0) {
> >                 /* Special case. POSIX says "test --help"
> >                  * should be no different from e.g. "test --foo".  */
> > //TODO: just compare applet_no with APPLET_NO_test
> >                 if (!ENABLE_TEST || strcmp(applet_name, "test") != 0)
> >                         bb_show_usage();
> >         }
> >         if (ENABLE_FEATURE_SUID)
> >                 check_suid(applet_no);
> >         exit(applet_main[applet_no](argc, argv));
> > }
> > 
> > Replace
> >         exit(applet_main[applet_no](argc, argv));
> > with
> >         printf("Entering applet_main\n");
> >         n = applet_main[applet_no](argc, argv);
> >         printf("Exited epplet_main, exiting...\n");
> >         exit(n);
> > 
> > and let me know hat do you see when you run "busybox true".
> > --
> > vda
> > 
> 
> Hi,
> after doing a little research and asking for advice the author of my phone's ROM
> I am now able to reproduce this behaviour consistently.
> 
> The busybox binary is built with buildroot-2009.05 (config attached)
> 
> The static uclibc binary i obtain is:
> debian:~/Desktop/buildroot-2009.05/project_build_arm/uclibc/busybox-1.14.1$ readelf  -h busybox
> ELF Header:
>   Magic:   7f 45 4c 46 01 01 01 03 04 00 00 00 00 00 00 00
>   Class:                             ELF32
>   Data:                              2's complement, little endian
>   Version:                           1 (current)
>   OS/ABI:                            UNIX - Linux
>   ABI Version:                       4
>   Type:                              EXEC (Executable file)
>   Machine:                           ARM
>   Version:                           0x1
>   Entry point address:               0x547cc
>   Start of program headers:          52 (bytes into file)
>   Start of section headers:          0 (bytes into file)
>   Flags:                             0x4000002, has entry point, Version4 EABI
>   Size of this header:               52 (bytes)
>   Size of program headers:           32 (bytes)
>   Number of program headers:         2
>   Size of section headers:           40 (bytes)
>   Number of section headers:         0
>   Section header string table index: 0
> 
> This can be compressed with the latest upx (needs also libucl):
>  hg pull from https://www.pysol.org:4443/hg/upx.hg
> 
> debian:~/Desktop/buildroot-2009.05/project_build_arm/uclibc/busybox-1.14.1$ ./upx.out busybox
>                        Ultimate Packer for eXecutables
>                           Copyright (C) 1996 - 2009
> UPX 3.04        Markus Oberhumer, Laszlo Molnar & John Reiser   Apr 27th 2009
> 
>         File size         Ratio      Format      Name
>    --------------------   ------   -----------   -----------
>     588552 ->    315564   53.62%   linux/armel   busybox
> 
> Packed 1 file.
> 
> After uploading it to the phone the binary shows the exact behaviour as expected:
> the requested command is executed but it never returns to the shell.
> The output of your debug code is:
> /system/sd/bin # ./busybox true
> 
> Entering applet_main
> Exited applet_main, exiting... (hangs forever)
> 
> 
> I've also tested changing exit to _exit
> 
> 	printf("Entering applet_main\n");
> 	n = applet_main[applet_no](argc, argv);
> 	printf("Exited applet_main, exiting...%d\n", n);
> 	_exit(n);
> 
> This seems to fix the bug:
> 
> ./busybox true
> Entering applet_main
> Exited applet_main, exiting...0
> 
> only if you call ./busybox with no args it hangs, but this is also
> fixed by changing:
> 
> void FAST_FUNC run_applet_and_exit(const char *name, char **argv)
> {
> 	int applet = find_applet_by_name(name);
> 	if (applet >= 0)
> 		run_applet_no_and_exit(applet, argv);
> 	if (!strncmp(name, "busybox", 7))
> -		exit(busybox_main(argv));
> +		_exit(busybox_main(argv));
> }
> 
> Same for when usage is shown.
> 
> So it seems that by changing exit with _exit everything works.
> Maybe  #define exit _exit ?

Well, in almost all cases exit == fflush(NULL) + _exit.
One difference is that it doesn't run atexit functions.

We use atexit in three applets only (sed, popmaildir, sendmail).

> Hope this helps you somehow to fix the bug if there is any
> (maybe it is just a toolchain issue??)

I think exit tries to walk a chain of magic sections
to run libc cleanup functions. It seems to be corrupted
by the compressor.

There is similar failure with --gc-sections + glibc: ld discards
these magic sections, and then exit does not flush the buffers,
because that is one of those cleanup functions.
--
vda


More information about the busybox mailing list