513 lines
9.3 KiB
Perl
Executable File
513 lines
9.3 KiB
Perl
Executable File
#!/usr/bin/perl
|
|
#
|
|
# Realtek Semiconductor Corp.
|
|
#
|
|
# rsdk-linux-lstrip: target fs libstrip
|
|
#
|
|
# Tony Wu (tonywu@realtek.com)
|
|
# Jan. 10, 2009
|
|
|
|
|
|
$| = 1;
|
|
use Cwd 'abs_path';
|
|
use File::Basename;
|
|
use Getopt::Long;
|
|
|
|
my @ROMFS_EXES = ();
|
|
my %ROMFS_LIBS = ();
|
|
my @LDS = ();
|
|
|
|
my $dir_rsdk;
|
|
my $dir_romfs;
|
|
my $var_lib_size1;
|
|
my $var_lib_size2;
|
|
my $var_bin_size1;
|
|
my $var_bin_size2;
|
|
|
|
my $ldscript;
|
|
my $libgcc;
|
|
my $endian;
|
|
|
|
##
|
|
## parse argument
|
|
##
|
|
&print_header();
|
|
|
|
if ($#ARGV != 0) {
|
|
&print_usage;
|
|
exit -1;
|
|
}
|
|
|
|
$dir_romfs = $ARGV[0];
|
|
|
|
print "INFO: shrinking ", $dir_romfs, "\n";
|
|
|
|
##
|
|
## 0.0
|
|
##
|
|
&list_romfs_exes;
|
|
&list_romfs_libs;
|
|
&list_used_libs;
|
|
|
|
$var_lib_size1 = &check_size("$dir_romfs/lib");
|
|
$var_bin_size1 = &check_size("$dir_romfs/bin");
|
|
|
|
##
|
|
## 0. locate RSDK directory/check libgcc/check endian
|
|
##
|
|
&check_rsdk('rsdk-linux-gcc');
|
|
&check_endian;
|
|
&check_libgcc;
|
|
|
|
##
|
|
## 2. regenerate libraries
|
|
##
|
|
&lib_makeover;
|
|
&lib_cleanup;
|
|
|
|
##
|
|
## strip/sstrip libraries and executables
|
|
##
|
|
&romfs_strip;
|
|
|
|
##
|
|
##
|
|
##
|
|
$var_lib_size2 = &check_size("$dir_romfs/lib");
|
|
$var_bin_size2 = &check_size("$dir_romfs/bin");
|
|
|
|
print "INFO: romfs stripping completed", "\n\n";
|
|
print "INFO: romfs/bin size reduced from $var_bin_size1 to $var_bin_size2", "\n";
|
|
print "INFO: romfs/lib size reduced from $var_lib_size1 to $var_lib_size2", "\n";
|
|
|
|
exit 0;
|
|
|
|
##
|
|
## EXE
|
|
##
|
|
sub list_used_libs
|
|
{
|
|
local($lib);
|
|
local($exe);
|
|
local($line);
|
|
local(@libs);
|
|
|
|
foreach $exe (@ROMFS_EXES) {
|
|
|
|
$line = &list_needed_libs($exe);
|
|
@libs = split(/:/, $line);
|
|
|
|
foreach $lib (@libs) {
|
|
$ROMFS_LIBS{$lib} .= $exe . ':';
|
|
}
|
|
}
|
|
|
|
while (1) {
|
|
$count = 0;
|
|
foreach $lib (keys(%ROMFS_LIBS)) {
|
|
next if ($ROMFS_LIBS{$lib} eq '');
|
|
|
|
$line = &list_needed_libs($lib);
|
|
@libs = split(/:/, $line);
|
|
|
|
foreach $exe (@libs) {
|
|
next if (index($ROMFS_LIBS{$exe}, $lib) != -1);
|
|
$ROMFS_LIBS{$exe} .= $lib . ':';
|
|
$count++;
|
|
}
|
|
}
|
|
last if ($count == 0);
|
|
}
|
|
}
|
|
|
|
sub list_romfs_exes
|
|
{
|
|
local(@FILE);
|
|
local($file);
|
|
|
|
print "INFO: listing romfs executables", "\n";
|
|
|
|
@FILE = ();
|
|
unshift @FILE, glob("$dir_romfs/bin/*")
|
|
if (!-l $dir_romfs . "/bin");
|
|
|
|
unshift @FILE, glob("$dir_romfs/usr/bin/*")
|
|
if (!-l $dir_romfs . "/usr" and !-l $dir_romfs . "/usr/bin");
|
|
|
|
unshift @FILE, glob("$dir_romfs/usr/sbin/*")
|
|
if (!-l $dir_romfs . "/usr" and !-l $dir_romfs . "/usr/sbin");
|
|
|
|
unshift @FILE, glob("$dir_romfs/sbin/*")
|
|
if (!-l $dir_romfs . "/sbin");
|
|
|
|
foreach $file (@FILE) {
|
|
next if (-l $file || !-B $file);
|
|
unshift @ROMFS_EXES, $file if (&test_if_elf($file));
|
|
}
|
|
}
|
|
|
|
sub list_romfs_libs
|
|
{
|
|
local(@FILE);
|
|
local($lib);
|
|
local($exe);
|
|
|
|
print "INFO: listing romfs shared libraries", "\n";
|
|
|
|
@FILE = ();
|
|
unshift @FILE, glob("$dir_romfs/lib/*.so*");
|
|
|
|
foreach $file (@FILE) {
|
|
next if (-l $file || !-B $file);
|
|
$ROMFS_LIBS{$file} = '' if (&test_if_elf($file));
|
|
}
|
|
}
|
|
|
|
sub list_needed_libs
|
|
{
|
|
local($lexe) = shift;
|
|
local($line) = '';
|
|
|
|
open(PIPE, "rsdk-linux-readelf -d $lexe 2>&1 | grep NEEDED |");
|
|
while (<PIPE>) {
|
|
chomp;
|
|
#0x00000001 (NEEDED) Shared library: [libc.so.0]
|
|
next if (!m|0x\d{8} \(NEEDED\).+Shared library: \[(.*)\]|);
|
|
|
|
$name = $1;
|
|
$file = $dir_romfs . '/lib/' . $name;
|
|
if (-l $file) {
|
|
$file = readlink($file);
|
|
$file = $dir_romfs . '/lib/' . $file;
|
|
}
|
|
|
|
next if (index($line, $file) != -1);
|
|
$line .= $file . ':';
|
|
}
|
|
close (PIPE);
|
|
|
|
return $line;
|
|
}
|
|
|
|
sub list_symbols
|
|
{
|
|
local(@exes) = @_;
|
|
local(%syms) = ();
|
|
local($exe);
|
|
local($sym);
|
|
|
|
%syms = ();
|
|
foreach $exe (@exes) {
|
|
open(PIPE, "rsdk-linux-nm --dynamic $exe 2>&1 |");
|
|
while (<PIPE>) {
|
|
if (m|rsdk-linux-nm:.*: No symbols|) {
|
|
print "WARNING: $exe contains no dynamic symbols", "\n";
|
|
%syms = ();
|
|
return %syms;
|
|
}
|
|
|
|
chomp;
|
|
if (m|^.{8} [BUV] (.+)|) {
|
|
$sym = $1;
|
|
$syms{$sym} = 1;
|
|
}
|
|
}
|
|
close(PIPE);
|
|
}
|
|
|
|
return %syms;
|
|
}
|
|
|
|
##
|
|
## library subroutines
|
|
##
|
|
sub generate_lds
|
|
{
|
|
local($lib_file) = shift;
|
|
local($lib_name) = shift;
|
|
local(%syms) = ();
|
|
local(@exes) = ();
|
|
local($line) = '';
|
|
|
|
##
|
|
## list symbols
|
|
##
|
|
$line = $ROMFS_LIBS{$lib_file};
|
|
@exes = split(/:/, $line);
|
|
%syms = &list_symbols(@exes);
|
|
if (%syms == ()) {
|
|
return -1;
|
|
}
|
|
|
|
##
|
|
## generate linker script
|
|
##
|
|
open(PIPE, ">$lib_name.lds");
|
|
print PIPE "INCLUDE $dir_rsdk/$ldscript", "\n";
|
|
|
|
for $sym (keys(%syms)) {
|
|
print PIPE "EXTERN($sym)", "\n" if ($syms{$sym} == 1);
|
|
}
|
|
|
|
close(PIPE);
|
|
|
|
unshift @LDS, "$lib_name.lds";
|
|
return 0;
|
|
}
|
|
|
|
sub generate_lib
|
|
{
|
|
local($lib_file) = shift;
|
|
local($lib_name) = shift;
|
|
|
|
local($sofile, $solink, $soname, @solibs);
|
|
local($line);
|
|
local($lcmd);
|
|
local($llib);
|
|
|
|
$sofile = $lib_file;
|
|
$solink = $lib_name . '.lds';
|
|
@solibs = ();
|
|
|
|
##
|
|
## list linked libraries
|
|
##
|
|
$line = &list_needed_libs($lib_file);
|
|
@solibs = split(/:/, $line);
|
|
|
|
##
|
|
## rebuild library
|
|
##
|
|
if ($lib_name eq 'libuClibc') {
|
|
$soname = 'libc.so.0';
|
|
unshift @solibs, $dir_rsdk . '/lib/uclibc_nonshared.a';
|
|
unshift @solibs, $dir_rsdk . '/lib/libc.a';
|
|
unshift @solibs, $libgcc unless ($libgcc eq '');
|
|
}
|
|
elsif ($lib_name =~ m|^libgcc|) {
|
|
return;
|
|
}
|
|
elsif ($lib_name =~ m|^ld|) {
|
|
return;
|
|
}
|
|
elsif ($lib_name =~ m|^libdl|) {
|
|
return;
|
|
}
|
|
elsif ($lib_name =~ m|^libpthread|) {
|
|
print "skipping $lib_name .\n";
|
|
return;
|
|
}
|
|
else {
|
|
$soname = $lib_name . '.so.0';
|
|
unshift @solibs, $dir_rsdk . '/lib/' . $lib_name . '.a';
|
|
unshift @solibs, $libgcc unless ($libgcc eq '');
|
|
}
|
|
|
|
##
|
|
## make sure each solib exists before we do regen.
|
|
##
|
|
foreach $llib (@solibs) {
|
|
if ($llib ne '' and !-e $llib) {
|
|
print "WARNING: $llib does not exist", "\n";
|
|
print "WARNING: skipping $lib_file regeneration", "\n";
|
|
return;
|
|
}
|
|
}
|
|
|
|
$lcmd = "rsdk-linux-gcc -nostdlib -Wl,-warn-common -shared -o $sofile ";
|
|
$lcmd .= "-Wl,-soname,$soname -Wl,--script=$solink @solibs";
|
|
|
|
system($lcmd);
|
|
}
|
|
|
|
sub lib_makeover
|
|
{
|
|
print "INFO: recreating shared libraries ...", "\n";
|
|
|
|
foreach $lib_file (keys(%ROMFS_LIBS)) {
|
|
|
|
if ($ROMFS_LIBS{$lib_file} eq '') {
|
|
next if ($lib_file =~ m/ld-uClibc/);
|
|
print "----> removing ", $lib_file, "\n";
|
|
system("rm -f $lib_file");
|
|
next;
|
|
}
|
|
|
|
print "----> shrinking ", $lib_file, "\n";
|
|
$lib_name = basename $lib_file;
|
|
$lib_name =~ m/^([^-\.]*)[-\.]/;
|
|
$lib_name = $1;
|
|
|
|
##
|
|
## generate LDS
|
|
##
|
|
$ret = &generate_lds($lib_file, $lib_name);
|
|
if ($ret < 0) {
|
|
print "WARNING: cannot rebuild library $lib_file, skipped", "\n";
|
|
next;
|
|
}
|
|
|
|
##
|
|
## generate LIB
|
|
##
|
|
&generate_lib($lib_file, $lib_name);
|
|
}
|
|
}
|
|
|
|
sub lib_cleanup
|
|
{
|
|
local(@FILE);
|
|
local($lib);
|
|
local($exe);
|
|
|
|
print "INFO: cleanup shared library links", "\n";
|
|
|
|
@FILE = ();
|
|
unshift @FILE, glob("$dir_romfs/lib/*.so*");
|
|
|
|
foreach $file (@FILE) {
|
|
if (-l $file) {
|
|
if (!-B $file) {
|
|
print "----> removing dangling link ", $file, "\n";
|
|
system("rm -f $file");
|
|
}
|
|
next;
|
|
}
|
|
|
|
unshift @ROMFS_EXES, $file if (&test_if_elf($file));
|
|
}
|
|
|
|
foreach $lds (@LDS) {
|
|
print "----> removing ldscript ", $lds, "\n";
|
|
system("rm -f $lds");
|
|
}
|
|
}
|
|
|
|
sub romfs_strip
|
|
{
|
|
local($file);
|
|
|
|
print "INFO: stripping executables and libraries", "\n";
|
|
|
|
foreach $file (@ROMFS_EXES) {
|
|
print "----> stripping ", $file, "\n";
|
|
system("rsdk-linux-strip $file");
|
|
system("rsdk-linux-sstrip $file");
|
|
}
|
|
}
|
|
|
|
##
|
|
## supporter functions
|
|
##
|
|
sub print_header
|
|
{
|
|
print "\n";
|
|
print "Realtek Semiconductor Corp.", "\n";
|
|
print "Shared Library Shrinker, v1.0", "\n";
|
|
print "Tony Wu (tonywu\@realtek.com)", "\n";
|
|
print "\n";
|
|
}
|
|
|
|
sub print_usage
|
|
{
|
|
print "\n";
|
|
print "usage: $0 dir_romfs", "\n";
|
|
print "\n";
|
|
}
|
|
|
|
sub check_rsdk
|
|
{
|
|
local($prog) = shift;
|
|
local($line);
|
|
|
|
$line = `which $prog 2>/dev/null`;
|
|
chomp $line;
|
|
|
|
if (!-f $line) {
|
|
print "ERROR: unable to allocate RSDK", "\n";
|
|
exit 1;
|
|
}
|
|
|
|
$line = `dirname $line`;
|
|
chomp $line;
|
|
|
|
if ($line eq '') {
|
|
print "ERROR: unable to allocate RSDK", "\n";
|
|
exit 1;
|
|
}
|
|
|
|
$dir_rsdk = abs_path($line . '/..');
|
|
print "INFO: RSDK -> ", $dir_rsdk, "\n";
|
|
}
|
|
|
|
sub check_endian
|
|
{
|
|
local($exe);
|
|
local($line);
|
|
|
|
print "INFO: checking romfs endian ... ";
|
|
$exe = $ROMFS_EXES[0];
|
|
if ($exe eq '') {
|
|
exit -1;
|
|
}
|
|
|
|
$line = `rsdk-linux-readelf -h $exe`;
|
|
if ($line =~ m/little endian/) {
|
|
$ldscript = "lib/ldscripts/elf32ltsmip.xs";
|
|
print "little endian";
|
|
}
|
|
|
|
if ($line =~ m/big endian/) {
|
|
$ldscript = "lib/ldscripts/elf32btsmip.xs";
|
|
print "big endian";
|
|
}
|
|
print "\n";
|
|
}
|
|
|
|
sub check_libgcc
|
|
{
|
|
local($lib);
|
|
|
|
print "INFO: checking libgcc ... ";
|
|
foreach $lib (keys(%ROMFS_LIBS)) {
|
|
if ($lib =~ m|libgcc|) {
|
|
print "shared", "\n";
|
|
$libgcc = ''; # follow shared libaray's instinct
|
|
return;
|
|
}
|
|
}
|
|
|
|
print "static", "\n";
|
|
$libgcc = $dir_rsdk . '/lib/libgcc.a';
|
|
}
|
|
|
|
sub check_size
|
|
{
|
|
local($line) = shift;
|
|
local($size, $rest);
|
|
|
|
return 0 if (!-d $line);
|
|
|
|
open(PIPE, "du -s $line |");
|
|
$line = <PIPE>;
|
|
close(PIPE);
|
|
|
|
($size, $rest) = split(/[\s\t]+/, $line);
|
|
return $size;
|
|
}
|
|
|
|
sub test_if_elf
|
|
{
|
|
local($file) = shift;
|
|
|
|
open(PIPE, $file);
|
|
read(PIPE, $line, 4);
|
|
close(PIPE);
|
|
|
|
if ($line eq "\x7FELF") {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|