#!/usr/bin/perl
#
# Realtek Semiconductor Corp.
#
# msdk-linux-lstrip: target file system strip
#
# 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 %MSDK_LIBS = ();
my %KEEP_SYMS = ();

my $var_eglibc = 0;
my $var_muslc = 0;
my $var_sdk = 'msdk';
my $ver_msdk;
my $dir_msdk;
my $dir_romfs = '';
my $dir_extfs = '';
my $var_romfs_size1;
my $var_romfs_size2;
my $var_strip = 2;

my $ldscript;
my $libgcc_file;
my $libgcc_static;
my $libpthread_used = 0;
my $endian;

##
## parse argument
##
&get_msdkver();
&print_header();
&check_option();

&vprint("INFO: shrinking ", $dir_romfs, "\n");
&vprint("INFO: shrinking ", $dir_extfs, "\n") if ($dir_extfs ne '');

##
## 0.0
##
&list_romfs;
&list_extfs if ($dir_extfs ne '');
&list_romfs_libs;

$var_romfs_size1 = &check_size("$dir_romfs/");
$var_romfs_size1+= &check_size("$dir_extfs/") if ($dir_extfs ne '');

##
## 0.1
##
&check_duplicate;

##
## 0. locate MSDK directory/check libgcc/check endian
##
&check_msdk('msdk-linux-gcc');
&check_endian;
&check_libgcc;

##
## 1. regenerate libraries
##
&list_msdk_libs;
&list_used_libs;
&lib_cleanup;
&lib_makeover unless ($opt_noshrink == 1);

##
## 2. strip/sstrip libraries and executables
##
&romfs_strip;

##
## 3. print stats
##
$var_romfs_size2 = &check_size("$dir_romfs/");
$var_romfs_size2+= &check_size("$dir_extfs/") if ($dir_extfs ne '');

&vprint("INFO: romfs stripping completed", "\n");
&vprint("INFO: romfs size reduced from $var_romfs_size1 to $var_romfs_size2", "\n");

exit 0;

##
## EXE
##
sub check_duplicate
{
  local($file);
  local(@item) = ();
  local(%inode) = ();
  local(@romfs_exes) = ();

  @romfs_exes = @ROMFS_EXES;
  @ROMFS_EXES = ();

  foreach $file (@romfs_exes) {
    @item = stat($file);
    if ($inode{$item[1]} == 1) {
      print $file, " is duplicate\n";
    } else {
      unshift @ROMFS_EXES, $file;
    }
    $inode{$item[1]} = 1;
  }
}

sub list_used_libs
{
  local($lib);
  local($exe);
  local($line);
  local(@libs);

  foreach $exe (@ROMFS_EXES) {

    $line = &list_needed_libs($exe, 0);
    @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, 0);
      @libs = split(/:/, $line);

      foreach $exe (@libs) {
        next if (index($ROMFS_LIBS{$exe}, $lib) != -1);
        $ROMFS_LIBS{$exe} .= $lib . ':';
        $count++;
      }
    }
    last if ($count == 0);
  }

  return;
}

sub list_extfs
{
    local(@FILE);
    local($file);
    local($type);

    @FILE = &glob_path($dir_extfs);
    foreach $file (@FILE) {
      next if (-l $file || !-B $file);

      $type = &check_elf_type($file);
      if ($type == 1 or $type == 2) {
        unshift @ROMFS_EXES, $file;
        next;
      }
    }
}

sub list_romfs
{
    local(@FILE);
    local($file);
    local($type);

    @FILE = &glob_path($dir_romfs);
    foreach $file (@FILE) {
      next if (-l $file || !-B $file);

      $type = &check_elf_type($file);
      if ($type == 1) {
        unshift @ROMFS_EXES, $file;
        next;
      }

      if ($type == 2) {
        next if ($file =~ m|$dir_romfs/lib/|);
        unshift @ROMFS_EXES, $file;
        next;
      }
    }
}

sub list_msdk_libs
{
  local(@FILE);
  local($file);
  local($name);

  &vprint("INFO: listing msdk shared libraries", "\n");

  @FILE = glob("$dir_msdk/lib/*.so*");
  foreach $file (@FILE) {
    next if (-l $file || !-B $file);
    $name = basename $file;
    $MSDK_LIBS{$name} = '1' if (&test_if_elf($file));
  }
}

sub list_romfs_libs
{
  local(@FILE);
  local($file);

  &vprint("INFO: listing romfs shared libraries", "\n");

  @FILE = glob("$dir_romfs/lib/*.so*");
  foreach $file (@FILE) {
    next if (-l $file || !-B $file);
    $ROMFS_LIBS{$file} = '' if (&test_if_elf($file));
  }
}

sub search_ld_script
{
  local($soname) = shift;
  local($script);

  # check static libraries of ld script if the script exists
  if ($soname =~ m|^(.*\.so)\.\d+$|) {
      $script = $dir_msdk . '/lib/' . $1;
      if (-f $script and not -l $script and not &test_if_elf($script)) {
        return $script;
      }
  }
  return '';
}

sub list_needed_libs
{
  local($lexe) = shift;
  local($link) = shift;
  local($line) = '';
  local($name);
  local($file);
  local(*PIPE);

  open(PIPE, "msdk-linux-readelf -d $lexe 2>&1 | grep NEEDED |");
  while (<PIPE>) {
    chomp;
    #0x00000001 (NEEDED)                     Shared library: [libc.so.0]
    next if (!m|0x\d+ \(NEEDED\).+Shared library: \[(.*)\]|);

    $name = $1;
    $file = &search_ld_script($name) if ($link == 1);

    if ($link == 0 or $file eq '') {
      $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(*PIPE);

  %syms = ();
  foreach $exe (@exes) {
    open(PIPE, "msdk-linux-nm --dynamic $exe 2>&1 |");
    while (<PIPE>) {
      if (m|msdk-linux-nm:.*: No symbols|) {
        print "WARNING: $exe contains no dynamic symbols", "\n";
        %syms = ();
        close(PIPE);
        return %syms;
      }

      chomp;
      if (m|^.+ [BU] (.+)|) {
        $syms{$1} = 1;
      }
    }
    close(PIPE);
  }

  return %syms;
}

sub list_lib_symbols
{
  local($lib_file) = shift;
  local(%lib_syms) = ();
  local(@sym);
  local(*PIPE);

  %lib_syms = ();
  open(PIPE, "msdk-linux-nm --dynamic $lib_file 2>&1 |");
  while (<PIPE>) {
    if (m|msdk-linux-nm:.*: No symbols|) {
      print "WARNING: $lib_file contains no dynamic symbols", "\n";
      last;
    }

    chomp;
    if (m|^[^\ ]+ . (.+)|) {
      $lib_syms{$1} = 1;
    }
  }
  close(PIPE);
  return %lib_syms;
}

sub read_soname
{
  local($lib_file) = shift;
  local($lib_name) = '';
  local(*PIPE);

  open(PIPE, "msdk-linux-readelf -d $lib_file 2>&1 | grep SONAME |");
  while (<PIPE>) {
    chomp;
    #0x0000000e (SONAME)                     Library soname: [libm.so.6]
    if (m|.+\(SONAME\).+Library soname: \[(.*)\]|) {
      $lib_name = $1;
      last;
    }
  }
  close (PIPE);

  return $lib_name;
}

sub read_soflag
{
  local($lib_file) = shift;
  local($lib_name) = '';
  local($lib_flag) = '';
  local(@lib_temp) = ();
  local(@lib_flag) = ();
  local(*PIPE);

  open(PIPE, "msdk-linux-readelf -d $lib_file 2>&1 | grep FLAGS |");
  while (<PIPE>) {
    chomp;
    #0x0000001e (FLAGS)                      STATIC_TLS
    #0x6ffffffb (FLAGS_1)                    Flags: NODELETE INITFIRST

    if (m|.+\(FLAGS_1\).+Flags: (.*)$|) {
      $lib_flag = $1;
      last;
    }
  }
  close (PIPE);

  @lib_temp = split(/\s/, $lib_flag);
  foreach $lib_flag (@lib_temp) {
    $lib_flag =~ s/ \t//g;
    next if ($lib_flag eq '');

    $lib_flag =~ tr/A-Z/a-z/;
    $lib_flag = '-Wl,-z,' . $lib_flag;
    unshift @lib_flag, $lib_flag;
  }

  return @lib_flag;
}

##
## library subroutines
##
sub generate_lds
{
  local($lib_file) = shift;
  local($lib_name) = shift;
  local(%lib_syms) = ();
  local(%syms) = ();
  local(@exes) = ();
  local($line) = '';
  local(*PIPE);
  local($mmac);
  local($keep_syms) = ();

  ##
  ## list my symbols
  ##
  %lib_syms = &list_lib_symbols($lib_file);
  if (%lib_syms == ()) {
    return -1;
  }

  ##
  ## list used 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");

  if ( -e "$dir_msdk/$ldscript" ) {
    print PIPE "INCLUDE $dir_msdk/$ldscript", "\n";
  }
  else {
    $mmac = `msdk-linux-gcc -dumpmachine`; chomp $mmac;
    print PIPE "INCLUDE $dir_msdk/$mmac/$ldscript", "\n";
  }

  if ($lib_name eq 'libc' || $lib_name eq 'libuClibc') {
    if ($libpthread_used == 0) {
      print PIPE "EXTERN(__pthread_cleanup_pop_restore)", "\n";
      print PIPE "EXTERN(__pthread_cleanup_push_defer)", "\n";
      print PIPE "EXTERN(_pthread_cleanup_pop_restore)", "\n";
      print PIPE "EXTERN(_pthread_cleanup_push_defer)", "\n";
      print PIPE "EXTERN(pthread_mutex_lock)", "\n";
      print PIPE "EXTERN(pthread_mutex_unlock)", "\n";
    }
    print PIPE "EXTERN(dl_iterate_phdr)", "\n";
    print PIPE "EXTERN(calloc)", "\n";
    print PIPE "EXTERN(__cxa_finalize)", "\n";
  }

  if ($lib_name eq 'libpthread') {
      print PIPE "EXTERN(__pthread_cleanup_pop_restore)", "\n";
      print PIPE "EXTERN(__pthread_cleanup_push_defer)", "\n";
      print PIPE "EXTERN(_pthread_cleanup_pop_restore)", "\n";
      print PIPE "EXTERN(_pthread_cleanup_push_defer)", "\n";
  }

  if ($KEEP_SYMS{$lib_name} ne '') {
    @keep_syms = split(/:/, $KEEP_SYMS{$lib_name});
    foreach $sym (@keep_syms) {
      print "INFO: lstrip keep symbol: $sym for $lib_name", "\n";
      print PIPE "EXTERN($sym)", "\n";
    }
  }

  for $sym (keys(%syms)) {
    print PIPE "EXTERN($sym)", "\n" if ($lib_syms{$sym} and $syms{$sym});
  }

  close(PIPE);
  return 0;
}

sub generate_lib
{
  local($lib_file) = shift;
  local($lib_name) = shift;

  local($sofile, $solink, $soname, @solibs, @soflag, @soitem);
  local($line);
  local($ldcmd);
  local($ldlib);

  $sofile = $lib_file;
  $solink = $lib_name . '.lds';
  @soitem = ();
  @soflag = ();

  ##
  ## list linked libraries
  ##
  $line = &list_needed_libs($lib_file, 1);
  @solibs = split(/:/, $line);

  $soname = &read_soname($lib_file);
  @soflag = &read_soflag($lib_file);

  ##
  ## rebuild library
  ##
  if ($lib_name eq 'libuClibc') {
    $soname = 'libc.so.0' if ($soname eq '');
    if (-e $dir_msdk . '/lib/uclibc_nonshared.a') {
      push @soitem, $dir_msdk . '/lib/uclibc_nonshared.a';
    }
    unshift @soitem, $dir_msdk . '/lib/libc_pic.a';
    push @soitem, $libgcc_file;
  }
  else {
    $soname = $lib_name . '.so.0' if ($soname eq '');
    if (-e $dir_msdk . '/lib/' . $lib_name . '_pic.a') {
      unshift @soitem, $dir_msdk . '/lib/' . $lib_name . '_pic.a';
    } else {
      unshift @soitem, $dir_msdk . '/lib/' . $lib_name . '.a';
    }
    if (-e $dir_msdk . '/lib/' . $lib_name . '_nonshared.a') {
      push @soitem, $dir_msdk . '/lib/' . $lib_name . '_nonshared.a';
    }
    if (-e $dir_msdk . '/lib/' . $lib_name . '_pic.map') {
      unshift @soflag, '-Wl,--version-script=' . $lib_name . '_pic.map';
    }
    push @solibs, $libgcc_file if ($libgcc_static);

    if ($lib_name =~ m|^libstdc\+\+|) {
      if (-f $dir_msdk . '/lib/libstdc++-symbols.ver') {
        unshift @soflag, '-Wl,--version-script=libstdc++-symbols.ver';
      }
    }
  }

  ##
  ## make sure each solib exists before we do regen.
  ##
  foreach $ldlib (@solibs,@soitem) {
    if ($ldlib ne '' and !-e $ldlib) {
      return;
    }
  }

  $ldcmd  = "msdk-linux-gcc -nostdlib -Wl,-warn-common -shared -o $sofile ";
  $ldcmd .= "-Wl,-init,__uClibc_init " if ($lib_name eq 'libuClibc');
  $ldcmd .= "-Wl,-soname,$soname -Wl,--script=$solink @soflag @soitem @solibs";
  print $ldcmd, "\n" if ($opt_debug);
  system($ldcmd);
}

sub parse_lstrip_keep
{
  local($keep_file) = abs_path () . '/.lstrip_keep';
  local($line);
  local($lib_name);
  local($symbol);
  local(*PIPE);

  return if (!-T $keep_file);

  print "INFO: read lstrip keep file", "\n";
  open(PIPE, "<$keep_file");
  while ($line = <PIPE>) {
    chomp $line;
    if (!($line =~ m|^\w+\s*:\s*\w+$|)) {
      print "WARNNING: invalid format \"$line\" in .lstrip_keep", "\n";
      print "Valid format: \"libc:symbol\", For example: \"libuClibc:lsearch\"", "\n";
      next;
    }
    $line =~ s/[\s]+//g;
    $lib_name = substr $line, 0, (index $line, ':');
    $symbol = substr $line, (index $line, ':') + 1;
    $KEEP_SYMS{$lib_name} .= $symbol . ':';
  }
  close (PIPE);
}

sub lib_makeover
{
  local($bookie_libc) = ();
  local($bookie_libcpp) = ();
  local($bookie_libld) = ();
  local($bookie_libdl) = ();
  local($bookie_libgcc) = ();
  local($bookie_libssp) = ();
  local($bookie_libmudflap) = ();
  local(@bookie_libs) = ();
  local($lib_file);
  local($lib_name);

  &vprint("INFO: shrinking shared libraries ...", "\n");

  foreach $lib_file (keys(%ROMFS_LIBS)) {
    $lib_name = basename $lib_file;
    next if ($ROMFS_LIBS{$lib_file} eq '');
    if ($lib_name =~ m|^libuClibc| or $lib_name =~ m|^libc-|) {
      $bookie_libc = $lib_file;
      next;
    }
    if ($lib_name =~ m|libstdc\+\+|) {
      $bookie_libcpp = $lib_file;
      next;
    }
    if ($lib_name =~ m|ld-|) {
      $bookie_libld = $lib_file;
      next;
    }
    if ($lib_name =~ m|libdl|) {
      $bookie_libdl = $lib_file;
      next;
    }
    if ($lib_name =~ m|libgcc|) {
      $bookie_libgcc = $lib_file;
      next;
    }
    if ($lib_name =~ m|libssp|) {
      $bookie_libssp = $lib_file;
      next;
    }
    if ($lib_name =~ m|libmudflap|) {
      $bookie_libmudflap = $lib_file;
      next;
    }

    if ($lib_name =~ m|libpthread|) {
      $libpthread_used = 1;
    }
    unshift @bookie_libs, $lib_file;
  }

  if ($bookie_libcpp ne '' and $bookie_libc ne '') {
    $ROMFS_LIBS{$bookie_libc} .= $bookie_libcpp . ':';
  }

  &parse_lstrip_keep;

  ##
  ## libdl is too much fun
  ##
  #&lib_shrinking($bookie_libgcc);
  #&lib_shrinking($bookie_libdl);
  #&lib_shrinking($bookie_libld);

  ##
  ## libc must be treated very differently
  ##
  &lib_shrinking($bookie_libc);

  foreach $lib_file (@bookie_libs) {
    &lib_shrinking($lib_file);
  }

  ##
  ## libstdc++ must be treated differently
  ##
  &lib_shrinking($bookie_libcpp) if ($bookie_libcpp ne '');
}

sub lib_shrinking
{
    local($lib_file) = shift;
    local($lib_name);
    local($ret);

    return if (!-e $lib_file);

    $lib_name = basename $lib_file;
    $lib_name =~ m/^([^-\.]*)[-\.]/;
    $lib_name = $1;

    ##
    ## generate LDS
    ##
    $ret = &generate_lds($lib_file, $lib_name);
    if ($ret < 0) {
      &vprint("----> skipping ", $lib_file, "\n");
      return;
    } else {
      &vprint("----> shrinking ", $lib_file, "\n");
    }

    ##
    ## generate LIB
    ##
    &generate_lib($lib_file, $lib_name);
    system("rm -f $lib_name.lds") if ($opt_debug == 0);
}

sub lib_cleanup
{
  local(@FILE);
  local($file);
  local($name);

  print("INFO: remove unused shared libraries", "\n");

  foreach $file (keys(%ROMFS_LIBS)) {
    $name = basename $file;
    if ($ROMFS_LIBS{$file} eq '' and ($opt_keep == 0 || $MSDK_LIBS{$name} eq '1')) {
      next if ($name =~ m/ld-/);
      next if ($name =~ m/libgcc/);
      next if ($name =~ m/libnss_files-/);
      next if ($name =~ m/libnss_dns-/);
      next if ($name =~ m/libresolv-/);
      print("----> removing ", $file, "\n");
      system("rm -f $file");
      next;
    }
  }

  print("INFO: cleanup shared library links", "\n");

  @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;
    }

    if (-T $file) {
      print("----> removing linker file ", $file, "\n");
      system("rm -f $file");
      next;
    }

    unshift @ROMFS_EXES, $file if (&test_if_elf($file));
  }
}

sub romfs_strip
{
  local($file);

  print("INFO: stripping executables and libraries", "\n");

  foreach $file (@ROMFS_EXES) {
    next if ($file eq '' or !-e $file);
    if ($var_strip == 2) {
      print("----> sstripping ", $file, "\n");
      system("msdk-linux-sstrip $file");
    }
    elsif ($var_strip == 1) {
      print("----> stripping ", $file, "\n");
      system("msdk-linux-strip $file");
    }
  }
}

##
## supporter functions
##
sub get_msdkver
{
    local($line);
    local(*PIPE);

    open(PIPE, 'msdk-linux-gcc -v 2>&1 |') 
       or die "ERROR: unable to execute msdk-linux-gcc";
    while ($line = <PIPE>) {
      chomp $line;
      if ($line =~ m|gcc version [^-]+-([^-]+)$|) {
        $ver_msdk = $1;
      }
    }
    close(PIPE);
}

sub print_header
{
    &vprint("\n");
    &vprint("Realtek Semiconductor Corp.", "\n");
    &vprint("Shared Library Shrinker, v1.1", "\n");
    &vprint("Tony Wu (tonywu\@realtek.com)", "\n");
    &vprint("\n");
    &vprint("MSDK version: ", $ver_msdk, "\n");
}

sub print_usage
{
    print "\n";
    print "usage: $0 [-nostrip|-nosstrip|-noshrink|-keep] dir_romfs", "\n";
    print "\n";
}

sub check_option
{
    local(@all_options) = (
        "help",
        "nostrip",
        "nosstrip",
        "noshrink",
        "keep",
        "debug",
    );

    $Getopt::Long::autoabbrev = 0;
    Getopt::Long::GetOptions(@all_options);

    if (defined($opt_nostrip)) {
        $var_strip = 0;
    }

    if (defined($opt_nosstrip) || $var_eglibc) {
        $var_strip = 1;
    }

    if (defined($opt_noshrink) || $var_eglibc || $var_muslc) {
        $opt_noshrink = 1;
    } else {
        $opt_noshrink = 0;
    }

    if (defined($opt_keep)) {
        $opt_keep = 1;
    } else {
        $opt_keep = 0;
    }

    if (defined($opt_v)) {
        $opt_v = 1;
    } else {
        $opt_v = 0;
    }

    if (defined($opt_debug)) {
        $opt_debug = 1;
    } else {
        $opt_debug = 0;
    }

    if ($#ARGV > 1 or $#ARGV < 0) {
        &print_usage;
        exit -1;
    }

    if ($#ARGV == 0) {
        $dir_romfs = $ARGV[0];
    }

    if ($#ARGV == 1) {
        $dir_romfs = $ARGV[0];
        $dir_extfs = $ARGV[1];
    }
}

sub check_msdk
{
    local($prog) = shift;
    local($line);

    $line = `which $prog 2>/dev/null`;
    chomp $line;

    if (!-f $line) {
        print "ERROR: unable to allocate MSDK", "\n";
        exit 1;
    }

    $line = `dirname $line`;
    chomp $line;

    if ($line eq '') {
        print "ERROR: unable to allocate MSDK", "\n";
        exit 1;
    }

    $dir_msdk = abs_path($line . '/..');
    &vprint("INFO: MSDK -> ", $dir_msdk, "\n");
}

sub check_endian
{
  local($exe);
  local($line);

  &vprint("INFO: checking romfs endian ... ");
  $exe = $ROMFS_EXES[0];
  if ($exe eq '') {
    print "ERROR: romfs is empty", "\n";
    exit -1;
  }

  $line = `msdk-linux-readelf -h $exe`;
  if ($line =~ m/little endian/) {
    $ldscript = "lib/ldscripts/elf32ltsmip.xs";
    &vprint("little endian");
  }

  if ($line =~ m/big endian/) {
    $ldscript = "lib/ldscripts/elf32btsmip.xs";
    &vprint("big endian");
  }
  &vprint("\n");
}

sub check_libgcc
{
  local($lib);

  $libgcc_file = $dir_msdk . '/lib/libgcc.a';
  $libgcc_static = 0;

  &vprint("INFO: checking libgcc ... ");
  foreach $lib (keys(%ROMFS_LIBS)) {
    if ($lib =~ m|libgcc|) {
      &vprint("shared", "\n");
      return;
    }
  }

  &vprint("static", "\n");
  $libgcc_static = 1;
}

sub check_size
{
  local($line) = shift;
  local($size, $rest);
  local(*PIPE);

  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;
  local(*PIPE);

  open(PIPE, $file);
  read(PIPE, $line, 4);
  close(PIPE);

  return ($line eq "\x7FELF");
}

sub check_elf_type
{
  local($file) = shift;
  local($line) = '';
  local(@LINE) = ();
  local(*PIPE);

  open(PIPE, "msdk-linux-readelf -h $file 2>&1 |") or die "ERROR: unable to readelf";
  @LINE = <PIPE>;
  close(PIPE);

  foreach $line (@LINE) {
    if ($line =~ m|EXEC \(Executable file\)|) {
      return 1;
    }

    if ($line =~ m|DYN \(Shared object file\)|) {
      return 2;
    }
  }

  return 0;
}

sub glob_path
{
  local($l_path) = shift;
  local($l_file) = '';
  local(@l_FILE) = ();
  local(*PIPE);

  open(PIPE, "find $l_path/ -type f |") or die "ERROR: unable to find";
  while ($l_file = <PIPE>) {
   chomp $l_file;
   unshift (@l_FILE, $l_file);
  }
  close(PIPE);

  return @l_FILE;
}

sub vprint
{
  print @_ if ($opt_v);
}
