#!/usr/bin/perl # # libfoo.pl # Copyright (C) 2006-2008 Jonathan Zarate # # - strip un-needed objects # - create xref of symbols used # sub error { print STDERR "\n*** ERROR: " . (shift) . "\n\n"; exit 1; } sub basename { my $fn = shift; if ($fn =~ /([^\/]+)$/) { return $1; } return $fn; } sub load { my $fname = shift; if ((-l $fname) || ($fname =~ /\/lib\/modules\/\d+\.\d+\.\d+/) || ($fname =~ /\.(asp|gif|png|svg|js|jsx|css|txt|pat|sh)$/)) { return; } if (-d $fname) { my $d; if (opendir($d, $fname)) { foreach (readdir($d)) { if ($_ !~ /^\./) { load($fname . "/" . $_); } } closedir($d); } return; } my $f; my $base; my $ok; my $s; $base = basename($fname); print LOG "\n\nreadelf $base:\n"; open($f, "mipsel-linux-readelf -WhsdD ${fname} 2>&1 |") || error("readelf - $!\n"); while (<$f>) { print LOG; if (/\s+Type:\s+(\w+)/) { $elf_type{$base} = $1; $ok = 1; last; } } if (!$ok) { close($f); return; } print "$elf_type{$base} $base", " " x 30, "\r"; push(@elfs, $base); while (<$f>) { print LOG; if (/\(NEEDED\)\s+Shared library: \[(.+)\]/) { push(@{$elf_lib{$base}}, $1); } elsif (/Symbol table for image:/) { last; } } while (<$f>) { print LOG; if (/\s+(WEAK|GLOBAL)\s+(?:DEFAULT|VISIBLE)\s+(\w+)\s+(\w+)/) { $s = $3; if ($2 eq 'UND') { if ($1 eq 'GLOBAL') { $elf_ext{$base}{$s} = 1; } else { print LOG "*** not GLOBAL\n"; } } elsif ($2 eq 'ABS') { } elsif ($2 =~ /^\d+$/) { $elf_exp{$base}{$s} = 1; } else { print LOG "*** unknown type\n"; } } elsif (!/Num Buc:/) { print LOG "*** strange line\n"; } } close($f); } sub fixDynDep { my ($user, $dep) = @_; if (!defined $elf_dyn{$user}{$dep}) { push(@{$elf_lib{$user}}, $dep); $elf_dyn{$user}{$dep} = 1; print LOG "FixDynDep: $user = $dep\n"; } } sub fixDyn { my $s; foreach (@elfs) { if (/^libipt_.+\.so$/) { fixDynDep("iptables", $_); } elsif (/^CP\d+\.so$/) { fixDynDep("smbd", $_); } } fixDynDep("l2tpd", "cmd.so"); fixDynDep("l2tpd", "sync-pppd.so"); # fixDynDep("libbcm.so", "libshared.so"); # fixDynDep("libbcm.so", "libc.so.0"); } sub usersOf { my $name = shift; my $sym = shift; my @x; my $e; my $l; @x = (); foreach $e (@elfs) { foreach $l (@{$elf_lib{$e}}) { if ($l eq $name) { if ((!defined $sym) || (defined $elf_ext{$e}{$sym})) { push(@x, $e); } last; } } } return @x; } sub resolve { my $name = shift; my $sym = shift; my $l; foreach $l (@{$elf_lib{$name}}) { # print "\n$l $sym ", $elf_exp{$l}{$sym}, "\n"; return $l if (defined $elf_exp{$l}{$sym}); } return "*** unresolved ***"; } sub fillGaps { my $name; my $sym; my @users; my $u; my $t; my $found; # print "Resolving implicit links...\n"; foreach $name (@elfs) { foreach $sym (keys %{$elf_ext{$name}}) { $found = 0; if (resolve($name, $sym) eq "*** unresolved ***") { @users = usersOf($name); foreach $u (@users) { # if exported by $u if (defined $elf_exp{$u}{$sym}) { fixDynDep($name, $u); $found = 1; } # if exported by shared libs of $u if (($t = resolve($u, $sym)) ne "*** unresolved ***") { fixDynDep($name, $t); $found = 1; } } if ($found == 0) { print "Unable to resolve $sym used by $name\n", @users; exit 1; } } } } } sub tab { my $current = shift; my $target = shift; my $s = ""; my $n; while (1) { $n = $current + (4 - ($current % 4)); last if ($n > $target); $s = $s . "\t"; $current = $n; } while ($current < $target) { $s = $s . " "; $current++; } return $s; } sub genXref { my $f; my $fname; my $s; my @u; # print "Generating Xref Report...\n"; open($f, ">libfoo_xref.txt"); foreach $fname (sort keys %elf_type) { print $f "$fname:\n"; if (scalar(@{$elf_lib{$fname}}) > 0) { print $f "Dependency:\n"; foreach $s (sort @{$elf_lib{$fname}}) { print $f "\t$s", defined $elf_dyn{$fname}{$s} ? " (dyn)\n" : "\n"; } } if (scalar(keys %{$elf_exp{$fname}}) > 0) { print $f "Export:\n"; foreach $s (sort keys %{$elf_exp{$fname}}) { @u = usersOf($fname, $s); if (scalar(@u) > 0) { print $f "\t$s", tab(length($s) + 4, 40), " > ", join(",", @u), "\n"; } else { print $f "\t$s\n"; } } } if (scalar(keys %{$elf_ext{$fname}}) > 0) { print $f "External:\n"; foreach $s (sort keys %{$elf_ext{$fname}}) { print $f "\t$s", tab(length($s) + 4, 40), " < ", resolve($fname, $s), "\n"; } } print $f "\n"; } close($f); } sub genSO { my ($so, $arc, $opt) = @_; my $name = basename($so); my $sym; my $fn; my $inuse; my @used; my @unused; my $cmd; my $before, $after; if (!-f $so) { print "$name: not found, skipping...\n"; return 0; } foreach $sym (sort keys %{$elf_exp{$name}}) { if (scalar(usersOf($name, $sym)) > 0) { push(@used, $sym); } else { push(@unused, $sym); } } # print "\n$name: Attempting to link ", scalar(@used), " and remove ", scalar(@unused), " objects...\n"; print LOG "\n\n${base}\n"; $cmd = "mipsel-uclibc-ld -shared -s -z combreloc --warn-common --fatal-warnings ${opt} -soname ${name} -o ${so}"; foreach (@{$elf_lib{$name}}) { if ((!$elf_dyn{$name}{$_}) && (/^lib(.+)\.so/)) { $cmd .= " -l$1"; } else { # print LOG "Not marking for linkage: $_\n"; } } # print "$cmd -u... ${arc}\n"; if (scalar(@used) == 0) { print "\n\n\n$name: WARNING: Library is not used by anything.\n\n\n"; <>; # return 0; } $cmd .= " -u " . join(" -u ", @used) . " ". $arc; print LOG "Command: $cmd\n"; print LOG "Used: ", join(",", @used), "\n"; print LOG "Unused: ", join(",", @unused), "\n"; $before = -s $so; system($cmd); if ($? != 0) { error("ld returned $?"); } $after = -s $so; print "$name: Attempted to remove ", scalar(@unused), "/", scalar(@unused) + scalar(@used), " symbols. "; printf "%.2fK - %.2fK = %.2fK\n", $before / 1024, $after / 1024, ($before - $after) / 1024; # print "\n$name: Attempting to link ", scalar(@used), " and remove ", scalar(@unused), " objects...\n"; # printf "Before: %.2fK / After: %.2fK / Removed: %.2fK\n\n", $before / 1024, $after / 1024, ($before - $after) / 1024; return ($before > $after) } ## ## ## # print "\nlibfoo.pl - fooify shared libraries\n"; # print "Copyright (C) 2006-2007 Jonathan Zarate\n\n"; $root = $ENV{"TARGETDIR"}; $uclibc = $ENV{"TOOLCHAIN"}; $router = $ENV{"SRCBASE"} . "/router"; if ((!-d $root) || (!-d $uclibc) || (!-d $router)) { print "Missing or invalid environment variables\n"; exit(1); } #open(LOG, ">libfoo.debug"); open(LOG, ">/dev/null"); print "Loading...\r"; load($root); print "Finished loading files.", " " x 30, "\r"; fixDyn(); fillGaps(); genXref(); genSO("${root}/lib/libc.so.0", "${uclibc}/lib/libc.a", "-init __uClibc_init ${uclibc}/lib/optinfo/interp.o"); genSO("${root}/lib/libresolv.so.0", "${uclibc}/lib/libresolv.a"); genSO("${root}/lib/libcrypt.so.0", "${uclibc}/lib/libcrypt.a"); genSO("${root}/lib/libm.so.0", "${uclibc}/lib/libm.a"); genSO("${root}/lib/libpthread.so.0", "${uclibc}/lib/libpthread.a"); genSO("${root}/lib/libutil.so.0", "${uclibc}/lib/libutil.a"); # genSO("${root}/lib/libdl.so.0", "${uclibc}/lib/libdl.a"); # genSO("${root}/lib/libnsl.so.0", "${uclibc}/lib/libnsl.a"); genSO("${root}/usr/lib/libcrypto.so", "${router}/openssl/libcrypto.a"); genSO("${root}/usr/lib/libzebra.so", "${router}/zebra/lib/libzebra.a"); # genSO("${root}/usr/lib/libtamba.so", "${router}/samba3/source/bin/libtamba.a"); # genSO("${root}/usr/lib/libiptc.so", "${router}/iptables/libiptc/libiptc.a"); # genSO("${root}/usr/lib/libshared.so", "${router}/shared/libshared.a"); # genSO("${root}/usr/lib/libnvram.so", "${router}/nvram/libnvram.a"); print "\n"; close(LOG); exit(0);