[Feature][Modem]Update MTK MODEM V1.6 baseline version: MOLY.NR15.R3.MD700.IVT.MP1MR3.MP.V1.6

MTK modem version: MT2735_IVT_MOLY.NR15.R3.MD700.IVT.MP1MR3.MP.V1.6.tar.gz
RF  modem version: NA

Change-Id: I45a4c2752fa9d1a618beacd5d40737fb39ab64fb
diff --git a/mcu/tools/partialLink.pl b/mcu/tools/partialLink.pl
new file mode 100644
index 0000000..de6d38c
--- /dev/null
+++ b/mcu/tools/partialLink.pl
@@ -0,0 +1,2689 @@
+#!/usr/bin/perl

+#

+#  Copyright Statement:

+#  --------------------

+#  This software is protected by Copyright and the information contained

+#  herein is confidential. The software may not be copied and the information

+#  contained herein may not be used or disclosed except with the written

+#  permission of MediaTek Inc. (C) 2011

+#

+#  BY OPENING THIS FILE, BUYER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES

+#  THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")

+#  RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO BUYER ON

+#  AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,

+#  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF

+#  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.

+#  NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE

+#  SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR

+#  SUPPLIED WITH THE MEDIATEK SOFTWARE, AND BUYER AGREES TO LOOK ONLY TO SUCH

+#  THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. MEDIATEK SHALL ALSO

+#  NOT BE RESPONSIBLE FOR ANY MEDIATEK SOFTWARE RELEASES MADE TO BUYER'S

+#  SPECIFICATION OR TO CONFORM TO A PARTICULAR STANDARD OR OPEN FORUM.

+#

+#  BUYER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND CUMULATIVE

+#  LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,

+#  AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,

+#  OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY BUYER TO

+#  MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.

+#

+#  THE TRANSACTION CONTEMPLATED HEREUNDER SHALL BE CONSTRUED IN ACCORDANCE

+#  WITH THE LAWS OF THE STATE OF CALIFORNIA, USA, EXCLUDING ITS CONFLICT OF

+#  LAWS PRINCIPLES.  ANY DISPUTES, CONTROVERSIES OR CLAIMS ARISING THEREOF AND

+#  RELATED THERETO SHALL BE SETTLED BY ARBITRATION IN SAN FRANCISCO, CA, UNDER

+#  THE RULES OF THE INTERNATIONAL CHAMBER OF COMMERCE (ICC).

+#

+#*****************************************************************************

+

+use strict;

+use warnings;

+use Cwd;

+use Cwd 'abs_path';

+use Data::Dumper;

+use File::Path;

+use File::Basename;

+use File::Compare;

+use File::Copy;

+use Getopt::Long;

+

+my @_argv = ($0, @ARGV);

+my $_argc = $#_argv + 1;

+

+# -----------------------------------------------------------------------------

+

+my $Version = "20110428";

+

+# -----------------------------------------------------------------------------

+

+# Workaround.

+my @ExcludedLib = (

+    # Function kal_mem_cpy will be placed in SECONDARY_ROM and be used before ready due to multiply definition from kal_release.h.

+    'custom.lib',

+    # In file variant.cpp, static variables of class type are used, and it results in indeterminate order of initialization.

+    'gadget_lib.lib',

+    # For customer do vendor release

+    'gis.lib',

+    'init.lib',

+    # Different bin files in MAUI

+    'che.lib',

+    'bootloader.lib',

+    'fota.lib',

+    'sst_sec.lib',

+    'ssf.lib',

+    'lqt.lib',

+    # ...

+);

+

+# Ignore the following libraries for checking (see checkLib).

+my %IgnoredLib = (

+    "verno.lib" => 1,

+);

+

+# -----------------------------------------------------------------------------

+

+# Set default command.

+my $Cmd_nm = "";

+my $Cmd_readelf = "";

+my $Cmd_strip = "";

+my $Cmd_armar = "armar";

+my $Cmd_armlink = "armlink";

+

+# -----------------------------------------------------------------------------

+# System Utilities

+

+# Set default debug level.

+my $DbgLevel = 1;

+

+# Set default info level to display message in the screen.

+my $InfoLevel = 0;

+

+# Set current directory.

+my $DirCur = cwd();

+

+# Set default temporary directory.

+my $DirTemp = $DirCur . "/pl_temp";

+

+# Set default log file.

+my $FileLog = $DirCur . "/" . basename(__FILE__) . ".log";

+unlink($FileLog);

+

+# Set default err file.

+my $FileErr = $DirCur . "/" . basename(__FILE__) . ".err";

+unlink($FileErr);

+

+# Set default temporary file.

+my $FileTmp = $DirCur . "/" . basename(__FILE__) . ".tmp";

+unlink($FileTmp);

+

+my $Current_time = time;

+

+# Write file specified by a global variable $FileLog.

+sub sysWriteLog

+{

+    my ($log) = @_;

+

+    if (open(FILE_LOG, ">>" . $FileLog))

+    {

+        print(FILE_LOG $log);

+        close(FILE_LOG);

+    }

+}

+

+sub sysWriteErr

+{

+    my ($log) = @_;

+

+    if (open(FILE_LOG, ">>" . $FileLog))

+    {

+        print(FILE_LOG $log);

+        close(FILE_LOG);

+    }

+    if (open(FILE_ERR, ">>" . $FileErr))

+    {

+        print(FILE_ERR $log);

+        close(FILE_ERR);

+    }

+}

+

+BEGIN

+{

+    my $_dying = 0;

+    my $_msg = "";

+    my $_ptrSub = 0;

+    my @_ptrArgs = ();

+

+    sub sysDieRegister

+    {

+        my ($ptrSub, @ptrArgs) = @_;

+

+        $_ptrSub = $ptrSub;

+        @_ptrArgs = @ptrArgs;

+    }

+

+    sub sysDie

+    {

+        my ($msg) = @_;

+

+        if (($_dying == 0) && $_ptrSub)

+        {

+            $_dying = 1;

+            $_msg = $msg;

+            $_ptrSub->(@_ptrArgs);

+            $_dying = 0;

+        }

+        system("date/T >>" . $FileLog);

+        system("time/T >>" . $FileLog);

+        $msg = $_msg . "\n" . $msg if ($_dying);

+        die($msg);

+    }

+}

+

+# -----------------------------------------------------------------------------

+# Basic Utilities

+

+sub utlDump

+{

+    my ($level, $ptr) = @_;

+

+    if (($level <= $InfoLevel) || ($level <= $DbgLevel))

+    {

+        my $msg = Dumper($ptr);

+

+        print($msg) if ($level <= $InfoLevel);

+        &sysWriteLog($msg) if ($level <= $DbgLevel);

+    }

+}

+

+sub utlSrcLoc

+{

+    my ($level) = @_;

+    my $filename = (caller($level + 1))[1];

+    my $line = (caller($level))[2];

+    my $subr = (caller($level + 1))[3];

+

+    $subr =~ s/^.*://;

+    return "[" . $filename . ":" . $subr . ":" . $line . "]";

+}

+

+sub utlLog

+{

+    my ($level, $msg) = @_;

+

+    if (($level <= $InfoLevel) || ($level <= $DbgLevel))

+    {

+        print($msg) if ($level <= $InfoLevel);

+        &sysWriteLog(&utlSrcLoc(1) . " " . $msg) if ($level <= $DbgLevel);

+    }

+}

+

+sub utlErr

+{

+    my ($level, $msg) = @_;

+

+    if (($level <= $InfoLevel) || ($level <= $DbgLevel))

+    {

+        print($msg) if ($level <= $InfoLevel);

+        &sysWriteErr(&utlSrcLoc(1) . " Error: " . $msg) if ($level <= $DbgLevel);

+    }

+}

+

+sub utlDie

+{

+    my ($msg, $level, $ignore) = @_;

+

+    $ignore = 0 if (not defined($ignore));

+    $level = 1 if (not defined($level));

+    $msg = &utlSrcLoc($level) . " Error: " . $msg . "\n";

+    &sysWriteErr($msg);

+    &sysDie($msg) unless ($ignore);

+}

+

+sub utlExec

+{

+    my ($cmd) = @_;

+    my $ret = "";

+

+    $cmd = $cmd . " 2>" . $FileTmp;

+    $ret = `$cmd`;

+

+    if ($? != 0)

+    {

+        my @stderr = ();

+

+        if (open(FILE_TEMP, $FileTmp))

+        {

+            @stderr = <FILE_TEMP>;

+            close(FILE_TEMP);

+        }

+        &utlDie("Fail to execute command: \"" . $cmd . "\"\nSTDOUT: " . $ret . "\nSTDERR: @stderr\n", 2);

+    }

+    return split(/\n/, $ret);

+}

+

+sub utlSystem

+{

+    my ($cmd, $ignore) = @_;

+    my $ret = "";

+    my $status = 0;

+

+    $ignore = 0 if (not defined($ignore));

+    $cmd = $cmd . " 2>" . $FileTmp;

+    $ret = `$cmd`;

+    $status = $?;

+

+    if ($status != 0)

+    {

+        my @stderr = ();

+        my $msg = "";

+

+        if (open(FILE_TMP, $FileTmp))

+        {

+            @stderr = <FILE_TMP>;

+            close(FILE_TMP);

+        }

+        if (($ret ne "") || ("@stderr" ne "") || ($ignore <= 0))

+        {

+            $msg = "Fail to execute command: \"" . $cmd . "\"\nSTDOUT: " . $ret . "\nSTDERR: @stderr\n";

+            print($msg) if ($ignore == 0);

+            &sysWriteErr(&utlSrcLoc(1) . " Error: " . $msg);

+        }

+    }

+    return $status;

+}

+

+# -----------------------------------------------------------------------------

+# Utilities

+

+sub utlTrim

+{

+    my ($str) = @_;

+

+    $str =~ s/^\s+//;

+    $str =~ s/\s+$//;

+

+    return $str;

+}

+

+sub utlAbsPath

+{

+    my ($path) = @_;

+

+    return abs_path($path);

+}

+

+sub utlRmTree

+{

+    my ($path, $ignore, $level) = @_;

+

+    if (-e $path)

+    {

+        my @list = glob($path . "/*");

+

+        $ignore = 1 if (not defined($ignore));

+        $level = 2 if (not defined($level));

+

+        foreach my $file (@list)

+        {

+            if (-d $file)

+            {

+                &utlRmTree($file, $ignore, $level + 1);

+            }

+            else

+            {

+                &utlDie("Fail to delete " . $file . " due to \"$!\"", $level, $ignore) unless (unlink($file));

+            }

+        }

+        &utlDie("Fail to delete " . $path . " due to \"$!\"", $level, $ignore) unless (rmdir($path));

+    }

+}

+

+sub utlMkDir

+{

+    my ($dir) = @_;

+

+    if (! -d $dir)

+    {

+        mkdir($dir) or &utlDie("Fail to create " . $dir . " due to \"$!\"", 2);

+    }

+}

+

+sub utlChDir

+{

+    my ($dir) = @_;

+

+    chdir($dir) or &utlDie("Fail to change directory to " . $dir . " due to \"$!\"", 2);

+}

+

+sub utlRmFile

+{

+    my ($file) = @_;

+

+    if (-e $file)

+    {

+        unlink($file) or &utlDie("Fail to delete " . $file . " due to \"$!\"", 2);

+    }

+}

+

+sub utlCpFile

+{

+    my ($src, $dst) = @_;

+

+    copy($src, $dst) or &utlDie("Fail to copy " . $src . " to " . $dst . " due to \"$!\"; please check available disk space", 2);

+}

+

+sub utlGetTimestamp

+{

+    my ($file) = @_;

+

+    return (stat($file))[9];

+}

+

+# -----------------------------------------------------------------------------

+

+sub copyFiles

+{

+    my ($dirSrc, $dirDst) = @_;

+    &utlDie("Cannot find " . $dirSrc) unless (-e $dirSrc);

+    &utlDie("Cannot find " . $dirDst) unless (-e $dirDst);

+

+    my @fileList = glob($dirSrc . "/*");

+

+    foreach my $src (@fileList)

+    {

+        &utlCpFile($src, $dirDst) if (-f $src);

+    }

+}

+

+sub copyLib

+{

+    my ($ptrAryDir, $srcSuffix, $dstSuffix) = @_;

+

+    foreach my $dirLib (@$ptrAryDir)

+    {

+        my $dirSrcLib = $dirLib . $srcSuffix;

+        my $dirDstLib = $dirLib . $dstSuffix;

+

+        &utlRmTree($dirDstLib);

+        &utlMkDir($dirDstLib);

+        &copyFiles($dirSrcLib, $dirDstLib);

+    }

+}

+

+sub generateCppLibFile

+{

+    my ($ptrHahLib, $fileCppLib) = @_;

+    my $ptrAryLibDir = $ptrHahLib->{ptrAryLibDir};

+    my $ptrHahLibUsage = $ptrHahLib->{ptrHahLibUsage};

+    my $ptrHahCppLibTab = $ptrHahLib->{ptrHahSymTable}->{ptrHahCppLibTab};

+

+    open(FILE_CPP_LIB, ">" . $fileCppLib) or &utlDie("Cannot open " . $fileCppLib);

+

+    foreach my $dirSrcLib (@$ptrAryLibDir)

+    {

+        my @libList = glob($dirSrcLib . "/*.lib");

+

+        foreach my $lib (@libList)

+        {

+            my $libName = basename($lib);

+

+            next if ((not exists($ptrHahLibUsage->{$libName})) || (not exists($ptrHahCppLibTab->{$libName})));

+            print(FILE_CPP_LIB $libName . "\n");

+        }

+    }

+

+    close(FILE_CPP_LIB);

+}

+

+sub readCppLibFile

+{

+    my ($fileCppLib, $ptrHahSymTable) = @_;

+    my $ptrHahCppLibTab = {};

+

+    &utlLog(1, "Reading C++ libraries from " . $fileCppLib . "\n");

+    open(FILE_CPP_LIB, "<" . $fileCppLib) or &utlDie("Cannot open " . $fileCppLib);

+

+    foreach my $line (<FILE_CPP_LIB>)

+    {

+        if ($line =~ /(\S+)/)

+        {

+            $ptrHahCppLibTab->{$1} = 1;

+        }

+    }

+

+    close(FILE_CPP_LIB);

+    $ptrHahSymTable->{ptrHahCppLibTab} = $ptrHahCppLibTab;

+}

+

+use constant {

+    SYM_DEF   => 1,

+    SYM_UNDEF => 2

+};

+

+sub addEntrySymToObj

+{

+    my ($ptrHahSymTable, $sym, $type, $lib, $obj) = @_;

+    my $ptrHahSymToObjTab = $ptrHahSymTable->{ptrHahSymToObjTab};

+

+    if (not exists($ptrHahSymToObjTab->{$sym}))

+    {

+        $ptrHahSymToObjTab->{$sym} = {};

+        $ptrHahSymToObjTab->{$sym}->{+SYM_DEF} = {};

+        $ptrHahSymToObjTab->{$sym}->{+SYM_UNDEF} = {};

+    }

+

+    my $ptrHahLibObj = $ptrHahSymToObjTab->{$sym}->{$type};

+    my $ptrHahMultiSymTab = $ptrHahSymTable->{ptrHahMultiSymTab};

+

+    if ($ptrHahMultiSymTab && ($type == SYM_DEF) && (scalar keys(%$ptrHahLibObj) > 0))

+    {

+        &utlLog(3, "Multiply defined symbol " . $sym . " in " . $lib . "(" . $obj . ")\n");

+        $ptrHahMultiSymTab->{$sym} = 1;

+    }

+

+    $ptrHahLibObj->{$lib} = {} if (not exists($ptrHahLibObj->{$lib}));

+    $ptrHahLibObj->{$lib}->{$obj} = 1;

+}

+

+sub loadSymTableByNm

+{

+    my ($ptrHahSymTable, $lib, $libName, $type) = @_;

+    my $ptrHahObjToSymTab = $ptrHahSymTable->{ptrHahObjToSymTab};

+    my $ptrHahCppLibTab = $ptrHahSymTable->{ptrHahCppLibTab};

+    my $obj = "";

+    my @symTable = ();

+

+    &utlDie("Assert") if (($type != SYM_DEF) && ($type != SYM_UNDEF));

+    if ($type == SYM_DEF)

+    {

+        @symTable = &utlExec($Cmd_nm . " -g --defined-only " . $lib);

+    }

+    else

+    {

+        @symTable = &utlExec($Cmd_nm . " -g -u " . $lib);

+    }

+

+    foreach my $line (@symTable)

+    {

+        if ($line =~ /^(\S+):\s*$/)

+        {

+            $obj = $1;

+            $ptrHahObjToSymTab->{$libName} = {} if (not exists($ptrHahObjToSymTab->{$libName}));

+            if (not exists($ptrHahObjToSymTab->{$libName}->{$obj}))

+            {

+                $ptrHahObjToSymTab->{$libName}->{$obj} = {};

+                $ptrHahObjToSymTab->{$libName}->{$obj}->{+SYM_DEF} = {};

+                $ptrHahObjToSymTab->{$libName}->{$obj}->{+SYM_UNDEF} = {};

+            }

+        }

+        elsif ($line =~ /\s+[^N]\s+(\S+)\s*$/)

+        {

+            my $sym = $1;

+            &utlDie("Assert") if ($obj eq "");

+            &utlLog(3, "Reading " . $sym . " from " . $libName . "(" . $obj . ")\n");

+            $ptrHahObjToSymTab->{$libName}->{$obj}->{$type}->{$sym} = 1;

+            &addEntrySymToObj($ptrHahSymTable, $sym, $type, $libName, $obj);

+            $ptrHahCppLibTab->{$libName} = 1 if (($type == SYM_DEF) && ($sym =~ /^_Z/));

+        }

+    }

+}

+

+sub loadSymTable

+{

+    my ($ptrHahLib, $fileCppLib) = @_;

+    my $ptrHahSymTable = $ptrHahLib->{ptrHahSymTable};

+

+    if (not exists($ptrHahSymTable->{ptrHahObjToSymTab}))

+    {

+        my $ptrAryLibDir = $ptrHahLib->{ptrAryLibDir};

+        my $ptrHahLibUsage = $ptrHahLib->{ptrHahLibUsage};

+

+        &utlLog(1, "Loading symbol tables...\n");

+

+        $ptrHahSymTable->{ptrHahObjToSymTab} = {};

+        $ptrHahSymTable->{ptrHahSymToObjTab} = {};

+        $ptrHahSymTable->{ptrHahCppLibTab} = {};

+        $ptrHahSymTable->{ptrHahMultiSymTab} = {};

+

+        foreach my $dirSrcLib (@$ptrAryLibDir)

+        {

+            my @libList = glob($dirSrcLib . "/*.lib");

+

+            foreach my $lib (@libList)

+            {

+                my $libName = basename($lib);

+

+                next if (not exists($ptrHahLibUsage->{$libName}));

+

+                &utlLog(1, "Loading " . $libName . "\n");

+                &loadSymTableByNm($ptrHahSymTable, $lib, $libName, SYM_DEF);

+                &loadSymTableByNm($ptrHahSymTable, $lib, $libName, SYM_UNDEF);

+            }

+        }

+        &utlLog(3, "Dumping object to symbol table...\n");

+        &utlDump(3, $ptrHahSymTable->{ptrHahObjToSymTab});

+        &utlLog(3, "Dumping symbol to object table...\n");

+        &utlDump(3, $ptrHahSymTable->{ptrHahSymToObjTab});

+

+        &generateCppLibFile($ptrHahLib, $fileCppLib);

+    }

+}

+

+sub generateDbgLibFile

+{

+    my ($ptrHahLib, $suffix, $ptrHahNoDbgLib, $fileDbgLib, $stripDebugCount) = @_;

+    my $ptrAryLibDir = $ptrHahLib->{ptrAryLibDir};

+    my $ptrHahLibUsage = $ptrHahLib->{ptrHahLibUsage};

+    my $ptrHahCppLibTab = $ptrHahLib->{ptrHahSymTable}->{ptrHahCppLibTab};

+    my $size = keys(%$ptrHahNoDbgLib);

+    my $count = 0;

+    my %libList = ();

+

+    foreach my $dirSrcLib (@$ptrAryLibDir)

+    {

+        my @list = glob($dirSrcLib . $suffix . "/*.lib");

+

+        foreach my $lib (@list)

+        {

+            my $libName = basename($lib);

+

+            $libList{$libName} = -s $lib;

+        }

+    }

+

+    open(FILE_DBG_LIB, ">" . $fileDbgLib) or &utlDie("Cannot open " . $fileDbgLib);

+

+    print(FILE_DBG_LIB "PLEASE ADD \";\" IN THE BEGINNING OF THE LINE IN ORDER TO REMOVE DEBUG INFO. OF A CORRESPONDING LIBRARY\n\n");

+

+    foreach my $libName (sort { $libList{$b} <=> $libList{$a} } (keys(%libList)))

+    {

+        if (not exists($ptrHahLibUsage->{$libName}))

+        {

+            &utlLog(1, "NOT stripping " . $libName . " due to no use\n");

+            next;

+        }

+        if (exists($ptrHahCppLibTab->{$libName}))

+        {

+            &utlLog(1, "NOT stripping " . $libName . " due to C++\n");

+            next;

+        }

+        if (($size == 0) && ($count < $stripDebugCount))

+        {

+            $ptrHahNoDbgLib->{$libName} = 1;

+            $count++;

+        }

+        if (exists($ptrHahNoDbgLib->{$libName}))

+        {

+            print(FILE_DBG_LIB "; " . $libName . " " . $libList{$libName} . "\n");

+        }

+        else

+        {

+            print(FILE_DBG_LIB $libName . " " . $libList{$libName} . "\n");

+        }

+    }

+

+    close(FILE_DBG_LIB);

+}

+

+sub readDbgLibFile

+{

+    my ($fileDbgLib, $ptrHahNoDbgLib) = @_;

+

+    &utlLog(1, "Reading debug-needed libraries from " . $fileDbgLib . "\n");

+

+    open(FILE_DBG_LIB, "<" . $fileDbgLib) or &utlDie("Cannot open " . $fileDbgLib);

+

+    foreach my $line (<FILE_DBG_LIB>)

+    {

+        if ($line =~ /^\s*;\s*(\S+)/)

+        {

+            $ptrHahNoDbgLib->{$1} = 1;

+        }

+    }

+

+    close(FILE_DBG_LIB);

+}

+

+sub removeDebugInfoGut

+{

+    my ($ptrHahLib, $suffix) = @_;

+    my $ptrAryLibDir = $ptrHahLib->{ptrAryLibDir};

+    my $ptrHahLibUsage = $ptrHahLib->{ptrHahLibUsage};

+    my $ptrHahNoDbgLib = $ptrHahLib->{ptrHahNoDbgLib};

+    my $ptrHahCppLibTab = $ptrHahLib->{ptrHahSymTable}->{ptrHahCppLibTab};

+    my $fileOpt = "__ARM_OPT__.txt";

+    my $cwd = cwd();

+

+    foreach my $dirSrcLib (@$ptrAryLibDir)

+    {

+        my @libList = glob($dirSrcLib . $suffix . "/*.lib");

+

+        foreach my $lib (@libList)

+        {

+            my $libName = basename($lib);

+

+            if (exists($ptrHahLibUsage->{$libName}) && (not exists($ptrHahCppLibTab->{$libName})) && (exists($ptrHahNoDbgLib->{$libName})))

+            {

+                &utlLog(1, "Removing debug info of " . $libName . "\n");

+                &utlMkDir($DirTemp);

+                &utlChDir($DirTemp);

+

+                my @objList = &utlExec($Cmd_armar . " -t " . $lib);

+

+                if (&utlSystem($Cmd_armar . " -x " . $lib) == 0)

+                {

+                    my $done = 1;

+

+                    foreach my $obj (@objList)

+                    {

+                        if (&utlSystem($Cmd_strip . " -d " . $obj) != 0)

+                        {

+                            $done = 0;

+                            last;

+                        }

+                    }

+                    if ($done)

+                    {

+                        open(FILE_OPT, ">" . $fileOpt) or &utlDie("Cannot open " . $fileOpt);

+                        print(FILE_OPT "--create " . $lib . " @objList");

+                        close(FILE_OPT);

+                        &utlSystem($Cmd_armar . " --via " . $fileOpt);

+                    }

+                }

+

+                &utlChDir($cwd);

+                &utlRmTree($DirTemp);

+            }

+        }

+    }

+}

+

+sub removeDebugInfo

+{

+    my ($ptrHahLib, $suffix, $fileCppLib, $fileDbgLib, $stripDebugCount) = @_;

+    my $update = scalar keys(%{$ptrHahLib->{ptrHahLibNew}});

+

+    if ((-e $fileCppLib) && ($update == 0))

+    {

+        &readCppLibFile($fileCppLib, $ptrHahLib->{ptrHahSymTable});

+    }

+    else

+    {

+        &loadSymTable($ptrHahLib, $fileCppLib);

+    }

+    &readDbgLibFile($fileDbgLib, $ptrHahLib->{ptrHahNoDbgLib}) if (-e $fileDbgLib);

+    &generateDbgLibFile($ptrHahLib, $suffix, $ptrHahLib->{ptrHahNoDbgLib}, $fileDbgLib, $stripDebugCount);

+    &removeDebugInfoGut($ptrHahLib, $suffix);

+}

+

+sub createDstLibDir

+{

+    my ($ptrHahLib) = @_;

+    my $ptrAryLibDir = $ptrHahLib->{ptrAryLibDir};

+    my $suffix = $ptrHahLib->{suffix};

+

+    foreach my $dirSrcLib (@$ptrAryLibDir)

+    {

+        my $dirDstLib = $dirSrcLib . $suffix;

+

+        &utlLog(1, "Creating " . $dirDstLib ."\n");

+        &utlRmTree($dirDstLib);

+        &utlMkDir($dirDstLib);

+    }

+}

+

+sub removeDstLibDir

+{

+    my ($ptrHahLib, $suffix, $dstSuffix) = @_;

+    my $ptrAryLibDir = $ptrHahLib->{ptrAryLibDir};

+

+    foreach my $dirSrcLib (@$ptrAryLibDir)

+    {

+        my $dirDstLib = $dirSrcLib . $suffix;

+

+        if (-e $dirDstLib)

+        {

+            if ((not defined($dstSuffix)) || ($dstSuffix eq ""))

+            {

+                &utlRmTree($dirDstLib);

+            }

+            else

+            {

+                my $path = $dirSrcLib . $dstSuffix;

+

+                &utlLog(1, "Removing " . $path . "\n");

+                &utlRmTree($path);

+                &utlLog(1, "Keeping partially-linked libraries in " . $path . "\n");

+                if (not rename($dirDstLib, $path))

+                {

+                    &utlLog(1, "Fail to rename directory " . $dirDstLib . " to " . $path . " due to \"$!\"\n");

+                    &utlLog(1, "Trying to copy files from " . $dirDstLib . " to " . $path . "\n");

+                    &utlMkDir($path);

+                    &copyFiles($dirDstLib, $path);

+                    &utlRmTree($dirDstLib);

+                }

+            }

+        }

+    }

+}

+

+sub finalizeLib

+{

+    my ($ptrHahLib) = @_;

+    my $ptrAryLibDir = $ptrHahLib->{ptrAryLibDir};

+    my $suffix = $ptrHahLib->{suffix};

+    my $suffix_bak = $ptrHahLib->{suffix_bak};

+

+    foreach my $dirSrcLib (@$ptrAryLibDir)

+    {

+        my $dirDstLib = $dirSrcLib . $suffix;

+        my $dirSrcBak = $dirSrcLib . $suffix_bak;

+

+        &utlDie("Cannot find " . $dirDstLib) unless (-e $dirDstLib);

+        &utlDie("Unexpected directory " . $dirSrcBak) if (-e $dirSrcBak);

+        &utlMkDir($dirSrcBak);

+        &copyFiles($dirSrcLib, $dirSrcBak);

+        &copyFiles($dirDstLib, $dirSrcLib);

+        &utlRmTree($dirSrcBak);

+        &utlRmTree($dirDstLib);

+    }

+}

+

+# -----------------------------------------------------------------------------

+

+sub isPattern

+{

+    my ($str) = @_;

+

+    return 1 if (($str ne "*") && ($str =~ /[*?]+/));

+    return 0;

+}

+

+sub regExp

+{

+    my ($expr) = @_;

+

+    $expr =~ s/\./\\./g;

+    $expr =~ s/\*/.*/g;

+    $expr =~ s/\?/./g;

+

+    return $expr;

+}

+

+sub hasRule

+{

+    my ($ptrHahRuleGut, $lib, $obj) = @_;

+

+    $lib = lc($lib);

+    $obj = lc($obj);

+    $lib = &regExp($lib) if (&isPattern($lib));

+    $obj = &regExp($obj) if (&isPattern($obj));

+    return 1 if (exists($ptrHahRuleGut->{$lib}) && exists($ptrHahRuleGut->{$lib}->{$obj}));

+    return 0;

+}

+

+sub loadRule

+{

+    my ($ptrHahRuleGut, $lib, $obj) = @_;

+

+    $lib = lc($lib);

+    $obj = lc($obj);

+    if (&isPattern($lib))

+    {

+        $ptrHahRuleGut->{regExpLib} = {} if (not exists($ptrHahRuleGut->{regExpLib}));

+        $lib = &regExp($lib);

+        $ptrHahRuleGut->{regExpLib}->{$lib} = 1;

+    }

+    $ptrHahRuleGut->{$lib} = {} if (not exists($ptrHahRuleGut->{$lib}));

+    if (&isPattern($obj))

+    {

+        $ptrHahRuleGut->{$lib}->{regExpObj} = {} if (not exists($ptrHahRuleGut->{$lib}->{regExpObj}));

+        $obj = &regExp($obj);

+        $ptrHahRuleGut->{$lib}->{regExpObj}->{$obj} = 1;

+    }

+    $ptrHahRuleGut->{$lib}->{$obj} = 1;

+}

+

+sub writeRule

+{

+    my ($hdlFileRule, $lib, $obj) = @_;

+

+    printf($hdlFileRule "%s(%s)\n", $lib, $obj) if ($hdlFileRule);

+}

+

+sub addRule

+{

+    my ($ptrHahRuleGut, $lib, $obj, $hdlFileRule) = @_;

+

+    if (not &hasRule($ptrHahRuleGut, $lib, $obj))

+    {

+        &loadRule($ptrHahRuleGut, $lib, $obj);

+        &writeRule($hdlFileRule, $lib, $obj);

+    }

+}

+

+# Return 1 if a library is specified in RULE to exclude; otherwise, return 0.

+sub isExcludedLib

+{

+    my ($ptrHahRuleGut, $lib) = @_;

+

+    $lib = lc($lib);

+    return 1 if (   (   exists($ptrHahRuleGut->{$lib})

+                     && exists($ptrHahRuleGut->{$lib}->{"*"}))

+                 || (   exists($ptrHahRuleGut->{"*"})

+                     && exists($ptrHahRuleGut->{"*"}->{"*"})));

+

+    foreach my $regExpLib (keys(%{$ptrHahRuleGut->{regExpLib}}))

+    {

+        if (($lib =~ /($regExpLib)/) && exists($ptrHahRuleGut->{$regExpLib}->{"*"}))

+        {

+            &loadRule($ptrHahRuleGut, $lib, "*");

+            return 1;

+        }

+    }

+    return 0;

+}

+

+# Return 1 if an object is specified in RULE to exclude; otherwise, return 0.

+sub isExcludedObj

+{

+    my ($ptrHahRuleGut, $lib, $obj) = @_;

+

+    $lib = lc($lib);

+    $obj = lc($obj);

+    return 1 if (   (   exists($ptrHahRuleGut->{$lib})

+                     && (   exists($ptrHahRuleGut->{$lib}->{$obj})

+                         || exists($ptrHahRuleGut->{$lib}->{"*"})))

+                 || (   exists($ptrHahRuleGut->{"*"})

+                     && exists($ptrHahRuleGut->{"*"}->{$obj}))

+                 || (   exists($ptrHahRuleGut->{"*"})

+                     && exists($ptrHahRuleGut->{"*"}->{"*"})));

+

+    if (   exists($ptrHahRuleGut->{"*"})

+ && exists($ptrHahRuleGut->{"*"}->{regExpObj}))

+    {

+        foreach my $regExpObj (keys(%{$ptrHahRuleGut->{"*"}->{regExpObj}}))

+        {

+            if ($obj =~ /($regExpObj)/)

+            {

+                &loadRule($ptrHahRuleGut, $lib, $obj);

+                return 1;

+            }

+        }

+    }

+

+    foreach my $regExpLib (keys(%{$ptrHahRuleGut->{regExpLib}}))

+    {

+        if ($lib =~ /($regExpLib)/)

+        {

+            if (exists($ptrHahRuleGut->{$regExpLib}->{$obj}))

+            {

+                &loadRule($ptrHahRuleGut, $lib, $obj);

+                return 1;

+            }

+            elsif (exists($ptrHahRuleGut->{$regExpLib}->{"*"}))

+            {

+                &loadRule($ptrHahRuleGut, $lib, "*");

+                return 1;

+            }

+            elsif (exists($ptrHahRuleGut->{$regExpLib}->{regExpObj}))

+            {

+                foreach my $regExpObj (keys(%{$ptrHahRuleGut->{$regExpLib}->{regExpObj}}))

+                {

+                    if ($obj =~ /($regExpObj)/)

+                    {

+                        &loadRule($ptrHahRuleGut, $lib, $obj);

+                        return 1;

+                    }

+                }

+            }

+        }

+    }

+    return 0;

+}

+

+sub readRuleFile

+{

+    my ($ptrHahRule) = @_;

+    my $fileRule = $ptrHahRule->{fileRule};

+    my $ptrHahRuleGut = $ptrHahRule->{ptrHahGut};

+

+    &utlLog(1, "Reading rule from " . $fileRule . "\n");

+

+    open(FILE_RULE, "<" . $fileRule) or &utlDie("Cannot open " . $fileRule);

+

+    foreach my $line (<FILE_RULE>)

+    {

+        # library(object)

+        if ($line =~ /^\s*([^\(\)\s]+)\s*\(\s*([^\(\)\s]+)\s*\)/)

+        {

+            my $lib = $1;

+            my $obj = $2;

+

+            &utlDie("Invalid rule: " . $line) if (($lib eq "*") && ($obj eq "*"));

+            &loadRule($ptrHahRuleGut, $lib, $obj) if (not &hasRule($ptrHahRuleGut, $lib, $obj));

+        }

+    }

+

+    close(FILE_RULE);

+

+    &utlCpFile($fileRule, $ptrHahRule->{file});

+}

+

+sub parseScatterFile

+{

+    my ($fileScatter, $ptrHahRule) = @_;

+    my $fileRule = $ptrHahRule->{file};

+    my $ptrHahRuleGut = $ptrHahRule->{ptrHahGut};

+

+    &utlLog(1, "Parsing scatter file " . $fileScatter . " and outputting rules to " . $fileRule . "\n");

+

+    open(FILE_RULE, ">" . $fileRule) or &utlDie("Cannot open " . $fileRule);

+    open(FILE_SCATTER, "<" . $fileScatter) or &utlDie("Cannot open " . $fileScatter);

+

+    foreach my $line (<FILE_SCATTER>)

+    {

+        if ($line =~ /^\s*(\S+\.o\S*)/i)

+        {

+            my $obj = $1;

+

+            next if ($obj =~ /^\*\.o/i);

+            &addRule($ptrHahRuleGut, "*", $obj, \*FILE_RULE);

+        }

+    }

+

+    close(FILE_SCATTER);

+    close(FILE_RULE);

+}

+

+sub generateFileNameList

+{

+    my ($file, $ptrAryFileName) = @_;

+

+    open(FILE_INPUT, "<" . $file) or &utlDie("Cannot open " . $file);

+

+    foreach my $line (<FILE_INPUT>)

+    {

+        my @optList = split(/\s+/, $line);

+

+        foreach my $opt (@optList)

+        {

+            push(@$ptrAryFileName, basename($1) . ".lib") if ($opt =~ /(\S+)\.lib(\(\S+\)|$)/);

+        }

+    }

+

+    close(FILE_INPUT);

+}

+

+sub readUserExclusionFile

+{

+    my ($ptrAryUserExclusionFile, $ptrHahRule) = @_;

+    my $fileRule = $ptrHahRule->{file};

+    my $ptrHahRuleGut = $ptrHahRule->{ptrHahGut};

+

+    open(FILE_RULE, ">>" . $fileRule) or &utlDie("Cannot open " . $fileRule);

+

+    foreach my $file (@$ptrAryUserExclusionFile)

+    {

+        my @nameList = ();

+

+        &utlLog(1, "Reading user exclusion from " . $file . "\n");

+

+        &generateFileNameList($file, \@nameList);

+        foreach my $lib (@nameList)

+        {

+            &utlLog(1, "Will exclude " . $lib . "\n");

+            &addRule($ptrHahRuleGut, $lib, "*", \*FILE_RULE);

+        }

+    }

+

+    close(FILE_RULE);

+}

+

+sub readExclusionFile

+{

+    my ($fileExclusion, $ptrHahRule) = @_;

+    my $fileRule = $ptrHahRule->{file};

+    my $ptrHahRuleGut = $ptrHahRule->{ptrHahGut};

+

+    &utlLog(1, "Reading exclusion from " . $fileExclusion . "\n");

+

+    open(FILE_EXCLUSION, "<" . $fileExclusion) or &utlDie("Cannot open " . $fileExclusion);

+    open(FILE_RULE, ">>" . $fileRule) or &utlDie("Cannot open " . $fileRule);

+

+    foreach my $line (<FILE_EXCLUSION>)

+    {

+        # library object

+        if ($line =~ /^\s*(\S+)\s+(\S+)/)

+        {

+            my $lib = $1;

+            my $obj = $2;

+

+            &utlLog(1, "Will exclude " . $lib . "(" . $obj . ")\n");

+            &addRule($ptrHahRuleGut, $lib, $obj, \*FILE_RULE);

+        }

+    }

+

+    close(FILE_RULE);

+    close(FILE_EXCLUSION);

+}

+

+sub writeExcludedLib

+{

+    my ($ptrAryExcludedLib, $ptrHahRule) = @_;

+    my $fileRule = $ptrHahRule->{file};

+    my $ptrHahRuleGut = $ptrHahRule->{ptrHahGut};

+

+    &utlLog(1, "Writing built-in excluded libraries to " . $fileRule . "\n");

+

+    open(FILE_RULE, ">>" . $fileRule) or &utlDie("Cannot open " . $fileRule);

+

+    foreach my $lib (@$ptrAryExcludedLib)

+    {

+        &utlLog(1, "Will exclude " . $lib . "\n");

+        &addRule($ptrHahRuleGut, $lib, "*", \*FILE_RULE);

+    }

+

+    close(FILE_RULE);

+}

+

+sub writeHint

+{

+    my ($hdlFileHint, $lib, $obj, $sym) = @_;

+

+    printf($hdlFileHint "%s %s %s\n", $lib, $sym, $obj) if ($hdlFileHint);

+}

+

+sub loadHint

+{

+    my ($ptrHahHintGut, $lib, $obj, $sym) = @_;

+

+    $ptrHahHintGut->{$sym} = {} if (not exists($ptrHahHintGut->{$sym}));

+    $ptrHahHintGut->{$sym}->{$obj} = {} if (not exists($ptrHahHintGut->{$sym}->{$obj}));

+    $ptrHahHintGut->{$sym}->{$obj}->{$lib} = 1;

+}

+

+sub addHint

+{

+    my ($ptrHahHintGut, $lib, $obj, $sym, $hdlFileHint) = @_;

+

+    if (   (not exists($ptrHahHintGut->{$sym}))

+        || (not exists($ptrHahHintGut->{$sym}->{$obj}))

+        || (not exists($ptrHahHintGut->{$sym}->{$obj}->{$lib})))

+    {

+        &loadHint($ptrHahHintGut, $lib, $obj, $sym);

+        &writeHint($hdlFileHint, $lib, $obj, $sym);

+    }

+}

+

+sub readHintFile

+{

+    my ($ptrHahHint, $ptrHahRule) = @_;

+    my $fileHint = $ptrHahHint->{fileHint};

+    my $ptrHahHintGut = $ptrHahHint->{ptrHahGut};

+    my $toRule = $ptrHahHint->{toRule};

+    my $fileRule = $ptrHahRule->{file};

+    my $ptrHahRuleGut = $ptrHahRule->{ptrHahGut};

+

+    &utlLog(1, "Reading hint from " . $fileHint . "\n");

+

+    open(FILE_HINT, "<" . $fileHint) or &utlDie("Cannot open " . $fileHint);

+    open(FILE_RULE, ">>" . $fileRule) or &utlDie("Cannot open " . $fileRule);

+

+    foreach my $line (<FILE_HINT>)

+    {

+        # library symbol object

+        if ($line =~ /^\s*(\S+)\s+(\S+)\s+(\S+)/)

+        {

+            my $lib = $1;

+            my $sym = $2;

+            my $obj = $3;

+

+            &loadHint($ptrHahHintGut, $lib, $obj, $sym);

+            &addRule($ptrHahRuleGut, $lib, $obj, \*FILE_RULE) if ($toRule);

+        }

+    }

+

+    close(FILE_RULE);

+    close(FILE_HINT);

+

+    &utlCpFile($fileHint, $ptrHahHint->{file});

+}

+

+sub generateLinkOptFile

+{

+    my ($ptrHahLib, $ptrHahRuleGut, $fileLinkOpt, $fileOutput) = @_;

+    my $ptrAryLibDir = $ptrHahLib->{ptrAryLibDir};

+    my $fileOrig = $fileOutput . ".orig";

+    my %libPath = ();

+

+    &utlCpFile($fileLinkOpt, $fileOrig);

+

+    foreach my $dirSrcLib (@$ptrAryLibDir)

+    {

+        my @libList = glob($dirSrcLib . "/*.lib");

+

+        foreach my $lib (@libList)

+        {

+            my $libName = basename($lib);

+

+            if (exists($libPath{$libName}))

+            {

+                $libPath{$libName} = "";

+            }

+            else

+            {

+                $libPath{$libName} = $dirSrcLib;

+            }

+        }

+    }

+

+    my $suffix = $ptrHahLib->{suffix};

+    my $ptrHahLibUsage = $ptrHahLib->{ptrHahLibUsage};

+

+    &utlLog(1, "Generating link option file " . $fileOutput . "\n");

+

+    open(FILE_LINK_OPT, "<" . $fileLinkOpt) or &utlDie("Cannot open " . $fileLinkOpt);

+    open(FILE_OUTPUT, ">" . $fileOutput) or &utlDie("Cannot open " . $fileOutput);

+

+    foreach my $line (<FILE_LINK_OPT>)

+    {

+        my @optList = split(/\s+/, $line);

+

+        foreach my $opt (@optList)

+        {

+            my $found = 0;

+

+            if ($opt =~ /\.lib$/)

+            {

+                my $libName = basename($opt);

+

+                if (exists($libPath{$libName}) && ($libPath{$libName} ne ""))

+                {

+                    if (($ptrHahRuleGut == 0) || (not &isExcludedLib($ptrHahRuleGut, $libName)))

+                    {

+                        my $lib = $libPath{$libName}. $suffix . "/" . $libName;

+

+                        $lib =~ s/\//\\/g;

+                        print(FILE_OUTPUT " " . $lib);

+                        $found = 1;

+                    }

+                    $ptrHahLibUsage->{$libName} = 1;

+                }

+            }

+            print(FILE_OUTPUT " " . $opt) unless ($found);

+        }

+        print(FILE_OUTPUT "\n");

+    }

+

+    close(FILE_OUTPUT);

+    close(FILE_LINK_OPT);

+

+    foreach my $libName (keys(%libPath))

+    {

+        &utlLog(1, "NOT using " . $libName . " in link option file " . $fileLinkOpt . "\n") if (not exists($ptrHahLibUsage->{$libName}));

+    }

+}

+

+# -----------------------------------------------------------------------------

+

+sub partialLinkLibGut

+{

+    my ($lib, $libName, $dirDstLib, $ptrHahLib, $ptrHahRuleGut, $hdlFileRule, $ptrHahHintGut, $hdlFileHint) = @_;

+    my @objList = &utlExec($Cmd_armar . " -t " . $lib);

+    my $linkedObj = "__" . $libName . "_linked__.obj";

+

+    &utlLog(3, "Analyzing " . $lib . " with @objList\n");

+

+    if (grep {$_ eq $linkedObj} @objList)

+    {

+        &utlLog(1, "Excluding " . $libName . " due to a linked object inside\n");

+        &utlCpFile($lib, $dirDstLib . "/" . $libName);

+    }

+    elsif (&isExcludedLib($ptrHahRuleGut, $libName))

+    {

+        my $dstLib = $dirDstLib . "/" . $libName;

+

+        &utlLog(1, "Excluding " . $libName . "\n");

+        &utlCpFile($lib, $dstLib);

+    }

+    else

+    {

+        my $ptrAryPLObj = [];

+        my $ptrAryNonPLObj = [];

+

+        # Find out objects which can be partially linked according to rule.

+        foreach my $obj (@objList)

+        {

+            if (&isExcludedObj($ptrHahRuleGut, $libName, $obj))

+            {

+                push(@$ptrAryNonPLObj, $obj);

+            }

+            else

+            {

+                push(@$ptrAryPLObj, $obj);

+            }

+        }

+        if ($#$ptrAryPLObj < 1)

+        {

+            &utlLog(1, "Excluding " . $libName . " due to one object for partial link\n");

+            &addRule($ptrHahRuleGut, $libName, "*", $hdlFileRule);

+            &addHint($ptrHahHintGut, $libName, "*", "*", $hdlFileHint);

+            &utlCpFile($lib, $dirDstLib . "/" . $libName);

+        }

+        else

+        {

+            my $fileOpt = "__ARM_OPT__.txt";

+            my $cwd = cwd();

+            my $err = 1;

+            my $cmd = "";

+

+            &utlRmTree($DirTemp);

+            &utlMkDir($DirTemp);

+            &utlChDir($DirTemp);

+

+            $cmd = $Cmd_armar . " -x " . $lib;

+            if (&utlSystem($cmd, -1) == 0)

+            {

+                # Partially link objects.

+                &utlDie("Assert: need at least 2 objects for partial link") unless ($#$ptrAryPLObj > 0);

+                $cmd = "@$ptrAryPLObj --partial --elf -o " . $linkedObj;

+                open(FILE_OPT, ">" . $fileOpt) or &utlDie("Cannot open " . $fileOpt);

+                print(FILE_OPT $cmd);

+                close(FILE_OPT);

+                &utlLog(1, $Cmd_armlink . " " . $cmd . "\n");

+                $cmd = $Cmd_armlink . " --via " . $fileOpt . " >>" . $FileLog . " 2>&1";

+                if (&utlSystem($cmd, -1) == 0)

+                {

+                    push(@$ptrAryNonPLObj, $linkedObj);

+

+                    # Archive objects to create a library.

+                    &utlLog(1, "Creating " . $libName . "\n");

+                    &utlLog(2, "Creating " . $libName . " with @$ptrAryNonPLObj\n");

+                    open(FILE_OPT, ">" . $fileOpt) or &utlDie("Cannot open " . $fileOpt);

+                    printf(FILE_OPT "--create %s/%s @$ptrAryNonPLObj", $dirDstLib, $libName);

+                    close(FILE_OPT);

+                    $cmd = $Cmd_armar . " --via " . $fileOpt . " >>" . $FileLog . " 2>&1";

+                    $err = 0 if (&utlSystem($cmd, -1) == 0);

+                }

+            }

+            if ($err)

+            {

+                if ((-e $fileOpt) && open(FILE_OPT, $fileOpt))

+                {

+                    my @temp = <FILE_OPT>;

+

+                    close(FILE_OPT);

+                    &utlErr(1, "Dumping " . $fileOpt . ": @temp\n");

+                }

+                &utlLog(1, "Excluding " . $libName . " due to failure to execute \"" . $cmd . "\"\n");

+                &utlCpFile($lib, $dirDstLib . "/" . $libName);

+            }

+

+            &utlChDir($cwd);

+            &utlRmTree($DirTemp);

+        }

+    }

+}

+

+sub partialLinkLib

+{

+    my ($ptrHahLib, $ptrHahRule, $ptrHahHint, $ptrHahLibErr) = @_;

+    my $ptrAryLibDir = $ptrHahLib->{ptrAryLibDir};

+    my $suffix = $ptrHahLib->{suffix};

+    my $fileRule = $ptrHahRule->{file};

+    my $ptrHahRuleGut = $ptrHahRule->{ptrHahGut};

+    my $fileHint = $ptrHahHint->{file};

+    my $ptrHahHintGut = $ptrHahHint->{ptrHahGut};

+

+    &utlLog(1, "Partially linking objects in the same libraries...\n");

+    open(FILE_RULE, ">>" . $fileRule) or &utlDie("Cannot open " . $fileRule);

+    open(FILE_HINT, ">>" . $fileHint) or &utlDie("Cannot open " . $fileHint);

+

+    if ($ptrHahLibErr && (scalar keys(%$ptrHahLibErr) > 0))

+    {

+        foreach my $libName (keys(%$ptrHahLibErr))

+        {

+            foreach my $dirSrcLib (@$ptrAryLibDir)

+            {

+                my $lib = $dirSrcLib . "/" . $libName;

+

+                if (-e $lib)

+                {

+                    my $dirDstLib = $dirSrcLib . $suffix;

+

+                    &utlDie("Cannot find " . $dirSrcLib) unless (-e $dirSrcLib);

+                    &utlDie("Cannot find " . $dirDstLib) unless (-e $dirDstLib);

+                    &partialLinkLibGut($lib, $libName, $dirDstLib, $ptrHahLib, $ptrHahRuleGut, \*FILE_RULE, $ptrHahHintGut, \*FILE_HINT);

+                }

+            }

+        }

+    }

+    else

+    {

+        my $ptrHahLibUsage = $ptrHahLib->{ptrHahLibUsage};

+

+        foreach my $dirSrcLib (@$ptrAryLibDir)

+        {

+            my @libList = glob($dirSrcLib . "/*.lib");

+            my $dirDstLib = $dirSrcLib . $suffix;

+

+            &utlDie("Cannot find directory " . $dirDstLib) unless (-e $dirDstLib);

+

+            foreach my $lib (@libList)

+            {

+                my $libName = basename($lib);

+

+                if (not exists($ptrHahLibUsage->{$libName}))

+                {

+                    my $dstLib = $dirDstLib . "/" . $libName;

+

+                    &utlLog(1, "Excluding " . $libName . " due to no use in the link option file\n");

+                    &utlCpFile($lib, $dstLib);

+                }

+                else

+                {

+                    &partialLinkLibGut($lib, $libName, $dirDstLib, $ptrHahLib, $ptrHahRuleGut, \*FILE_RULE, $ptrHahHintGut, \*FILE_HINT);

+                }

+            }

+        }

+    }

+

+    close(FILE_HINT);

+    close(FILE_RULE);

+}

+

+use constant {

+    LINK_NONE       =>  2,

+    LINK_OK_STRIP   =>  1,

+    LINK_OK         =>  0,

+    LINK_OUT_OF_MEM => -1,

+    LINK_UNKNOWN    => -2,

+    LINK_FAIL       => -3,

+};

+

+# Parse an error file from final link.

+sub parseErrorFile

+{

+    my ($ptrHahError) = @_;

+    my $fileError = $ptrHahError->{file};

+    my $ret = LINK_OK;

+

+    $ptrHahError->{ptrHahObjSymErr} = {};

+    $ptrHahError->{ptrHahLibSymErr} = {};

+    $ptrHahError->{ptrHahLibObjRef} = {};

+    $ptrHahError->{ptrHahLibErr} = {};

+

+    &utlLog(1, "Parsing linking error from " . $fileError . "\n");

+    open(FILE_ERROR, "<" . $fileError) or &utlDie("Cannot open " . $fileError);

+

+    my $ptrHahObjSymErr = $ptrHahError->{ptrHahObjSymErr};

+    my $ptrHahLibSymErr = $ptrHahError->{ptrHahLibSymErr};

+

+    foreach my $line (<FILE_ERROR>)

+    {

+        if ($line =~ /^Error: L6218E: Undefined symbol (\S+) \(referred from __(\S+)_linked__.obj\)/)

+        {

+            $ptrHahLibSymErr->{$2} = {} if (not exists($ptrHahLibSymErr->{$2}));

+            $ptrHahLibSymErr->{$2}->{$1} = SYM_UNDEF if (not exists($ptrHahLibSymErr->{$2}->{$1}));

+            $ret = LINK_FAIL;

+        }

+        elsif ($line =~ /^Error: L6218E: Undefined symbol (\S+) \(referred from (\S+)\)/)

+        {

+            $ptrHahObjSymErr->{$2} = {} if (not exists($ptrHahObjSymErr->{$2}));

+            $ptrHahObjSymErr->{$2}->{$1} = SYM_UNDEF if (not exists($ptrHahObjSymErr->{$2}->{$1}));

+            $ret = LINK_FAIL;

+        }

+        elsif ($line =~ /^Error: L6200E: Symbol (\S+) multiply defined \(by __(\S+)_linked__.obj and __(\S+)_linked__.obj\)/)

+        {

+            $ptrHahLibSymErr->{$2} = {} if (not exists($ptrHahLibSymErr->{$2}));

+            $ptrHahLibSymErr->{$2}->{$1} = SYM_DEF if (not exists($ptrHahLibSymErr->{$2}->{$1}));

+            if ($2 ne $3)

+            {

+                $ptrHahLibSymErr->{$3} = {} if (not exists($ptrHahLibSymErr->{$3}));

+                $ptrHahLibSymErr->{$3}->{$1} = SYM_DEF if (not exists($ptrHahLibSymErr->{$3}->{$1}));

+            }

+            $ret = LINK_FAIL;

+        }

+        elsif ($line =~ /^Error: L6200E: Symbol (\S+) multiply defined \(by __(\S+)_linked__.obj and (\S+)\)/)

+        {

+            $ptrHahLibSymErr->{$2} = {} if (not exists($ptrHahLibSymErr->{$2}));

+            $ptrHahLibSymErr->{$2}->{$1} = SYM_DEF if (not exists($ptrHahLibSymErr->{$2}->{$1}));

+            $ret = LINK_FAIL;

+        }

+        elsif ($line =~ /^Error: L6200E: Symbol (\S+) multiply defined \(by (\S+) and __(\S+)_linked__.obj\)/)

+        {

+            $ptrHahLibSymErr->{$3} = {} if (not exists($ptrHahLibSymErr->{$3}));

+            $ptrHahLibSymErr->{$3}->{$1} = SYM_DEF if (not exists($ptrHahLibSymErr->{$3}->{$1}));

+            $ret = LINK_FAIL;

+        }

+        elsif ($line =~ /^Error: L6200E: Symbol (\S+) multiply defined \(by (\S+) and (\S+)\)/)

+        {

+            $ptrHahObjSymErr->{$2} = {} if (not exists($ptrHahObjSymErr->{$2}));

+            $ptrHahObjSymErr->{$2}->{$1} = SYM_DEF if (not exists($ptrHahObjSymErr->{$2}->{$1}));

+            $ptrHahObjSymErr->{$3} = {} if (not exists($ptrHahObjSymErr->{$3}));

+            $ptrHahObjSymErr->{$3}->{$1} = SYM_DEF if (not exists($ptrHahObjSymErr->{$3}->{$1}));

+            $ret = LINK_FAIL;

+        }

+        elsif ($line =~ /^Fatal error: L6000U: Out of memory/)

+        {

+            &utlLog(1, $line);

+            $ret = LINK_OUT_OF_MEM;

+            last;

+        }

+        elsif (   ($line =~ /^Error:/)

+               || ($line =~ /^Fatal error:/))

+        {

+            &utlLog(0, $line);

+            $ret = LINK_UNKNOWN;

+            last;

+        }

+    }

+

+    close(FILE_ERROR);

+    return $ret;

+}

+

+sub finalLink

+{

+    my ($extraOption, $fileLinkOption, $ptrHahError) = @_;

+    my $fileError = $ptrHahError->{file};

+    my $fileOutput = $ptrHahError->{fileOutput};

+    my $cmd = $Cmd_armlink . " " . $extraOption . " --via " . $fileLinkOption . " --errors " . $fileError;

+

+    &utlLog(1, "Executing \"" . $cmd . "\"\n");

+    &utlSystem($cmd, 1);

+    &utlCpFile($fileError, $fileOutput) unless ($fileOutput eq "");

+    return &parseErrorFile($ptrHahError);

+}

+

+BEGIN

+{

+    my $hasExcludedRedefinedSym = 0;

+

+    sub ruleOutObjWithRedefinedSym

+    {

+        my ($ptrHahSymTable, $ptrHahRule, $ptrHahHint, $ptrHahLibErr) = @_;

+        my $ptrHahMultiSymTab = $ptrHahSymTable->{ptrHahMultiSymTab};

+

+        if (($hasExcludedRedefinedSym == 0) && $ptrHahMultiSymTab && (scalar keys(%$ptrHahMultiSymTab) > 0))

+        {

+            my $ptrHahSymToObjTab = $ptrHahSymTable->{ptrHahSymToObjTab};

+            my $fileRule = $ptrHahRule->{file};

+            my $ptrHahRuleGut = $ptrHahRule->{ptrHahGut};

+            my $fileHint = $ptrHahHint->{file};

+            my $ptrHahHintGut = $ptrHahHint->{ptrHahGut};

+

+            open(FILE_RULE, ">>" . $fileRule) or &utlDie("Cannot open " . $fileRule);

+            open(FILE_HINT, ">>" . $fileHint) or &utlDie("Cannot open " . $fileHint);

+

+            foreach my $sym (keys(%$ptrHahMultiSymTab))

+            {

+                if ($sym !~ /^_Z/)

+                {

+                    my $ptrHahLibObj = $ptrHahSymToObjTab->{$sym}->{+SYM_DEF};

+

+                    foreach my $lib (keys(%$ptrHahLibObj))

+                    {

+                        if (not &hasRule($ptrHahRuleGut, $lib, "*"))

+                        {

+                            foreach my $obj (keys(%{$ptrHahLibObj->{$lib}}))

+                            {

+                                if (not &hasRule($ptrHahRuleGut, $lib, $obj))

+                                {

+                                    &utlLog(1, "Adding rule to exclude " . $lib . "(" . $obj . ") due to redefinition of " . $sym . "\n");

+                                    &addRule($ptrHahRuleGut, $lib, $obj, \*FILE_RULE);

+                                    &addHint($ptrHahHintGut, $lib, $obj, $sym, \*FILE_HINT);

+                                    $ptrHahLibErr->{$lib} = 1;

+                                }

+                            }

+                        }

+                    }

+                }

+            }

+

+            close(FILE_HINT);

+            close(FILE_RULE);

+

+            $hasExcludedRedefinedSym = 1;

+        }

+    }

+}

+

+sub ruleOutCppLib

+{

+    my ($ptrHahSymTable, $ptrHahRule, $ptrHahHint, $ptrHahLibErr) = @_;

+    my $ptrHahCppLibTab = $ptrHahSymTable->{ptrHahCppLibTab};

+

+    if ($ptrHahCppLibTab && (scalar keys(%$ptrHahCppLibTab) > 0))

+    {

+        my $fileRule = $ptrHahRule->{file};

+        my $ptrHahRuleGut = $ptrHahRule->{ptrHahGut};

+        my $fileHint = $ptrHahHint->{file};

+        my $ptrHahHintGut = $ptrHahHint->{ptrHahGut};

+

+        open(FILE_RULE, ">>" . $fileRule) or &utlDie("Cannot open " . $fileRule);

+        open(FILE_HINT, ">>" . $fileHint) or &utlDie("Cannot open " . $fileHint);

+

+        foreach my $lib (keys(%$ptrHahCppLibTab))

+        {

+            if (not &hasRule($ptrHahRuleGut, $lib, "*"))

+            {

+                &utlLog(1, "Adding rule to exclude " . $lib . " due to C++\n");

+                &addRule($ptrHahRuleGut, $lib, "*", \*FILE_RULE);

+                &addHint($ptrHahHintGut, $lib, "*", "*", \*FILE_HINT);

+                $ptrHahLibErr->{$lib} = 1;

+            }

+        }

+

+        close(FILE_HINT);

+        close(FILE_RULE);

+    }

+}

+

+BEGIN

+{

+    my $hasExcludedCppLib = 0;

+

+    sub handleObjSymErr

+    {

+        my ($ptrHahSymTable, $ptrHahRule, $ptrHahHint, $ptrHahError, $excludeCppLib) = @_;

+        my $ptrHahObjSymErr = $ptrHahError->{ptrHahObjSymErr};

+

+        if (scalar keys(%$ptrHahObjSymErr) > 0)

+        {

+            my $ptrHahSymToObjTab = $ptrHahSymTable->{ptrHahSymToObjTab};

+            my $ptrHahCppLibTab = $ptrHahSymTable->{ptrHahCppLibTab};

+            my $ptrHahLibObjRef = $ptrHahError->{ptrHahLibObjRef};

+

+            foreach my $obj (keys(%$ptrHahObjSymErr))

+            {

+                foreach my $sym (keys(%{$ptrHahObjSymErr->{$obj}}))

+                {

+                    my $type = $ptrHahObjSymErr->{$obj}->{$sym};

+                    my $ptrHahLibObj = $ptrHahSymToObjTab->{$sym}->{$type};

+

+                    foreach my $lib (keys(%$ptrHahLibObj))

+                    {

+                        if (exists($ptrHahLibObj->{$lib}->{$obj}))

+                        {

+                            if (($hasExcludedCppLib == 1) || ($excludeCppLib == 0) || ($ptrHahCppLibTab == 0) || (not exists($ptrHahCppLibTab->{$lib})))

+                            {

+                                &utlLog(1, "Finding " . $obj . " with " . $sym . " from " . $lib . "\n");

+                                $ptrHahLibObjRef->{$lib} = {} if (not exists($ptrHahLibObjRef->{$lib}));

+                                $ptrHahLibObjRef->{$lib}->{$obj} = 1;

+                            }

+                        }

+                    }

+                }

+            }

+            if (($hasExcludedCppLib == 0) && $excludeCppLib)

+            {

+                &ruleOutCppLib($ptrHahSymTable, $ptrHahRule, $ptrHahHint, $ptrHahError->{ptrHahLibErr});

+                $hasExcludedCppLib = 1;

+            }

+        }

+    }

+}

+

+sub examineLibGut

+{

+    my ($lib, $ptrHahSymTable, $ptrHahRuleGut, $hdlFileRule, $ptrHahHintGut, $hdlFileHint, $ptrHahError) = @_;

+    my $ptrHahSymToObjTab = $ptrHahSymTable->{ptrHahSymToObjTab};

+    my $ptrHahSymErr = $ptrHahError->{ptrHahLibSymErr}->{$lib};

+    my $ptrHahLibErr = $ptrHahError->{ptrHahLibErr};

+

+    if (not &hasRule($ptrHahRuleGut, $lib, "*"))

+    {

+        &utlLog(1, "Examining " . $lib . "\n");

+

+        foreach my $sym (keys(%$ptrHahSymErr))

+        {

+            my $type = $ptrHahSymErr->{$sym};

+

+            foreach my $obj (keys(%{$ptrHahSymToObjTab->{$sym}->{$type}->{$lib}}))

+            {

+                if (not &hasRule($ptrHahRuleGut, $lib, $obj))

+                {

+                    &utlLog(1, "Adding rule to exclude " . $lib . "(" . $obj . ") due to error symbol " . $sym . "\n");

+                    &addRule($ptrHahRuleGut, $lib, $obj, $hdlFileRule);

+                    &addHint($ptrHahHintGut, $lib, $obj, $sym, $hdlFileHint);

+                    $ptrHahLibErr->{$lib} = 1;

+                }

+            }

+        }

+    }

+}

+

+sub examineLibGutExt

+{

+    my ($libRef, $ptrHahSymTable, $ptrHahRuleGut, $hdlFileRule, $ptrHahHintGut, $hdlFileHint, $ptrHahError, $ptrHahRefObjSet) = @_;

+    my $ptrHahObjToSymTab = $ptrHahSymTable->{ptrHahObjToSymTab};

+    my $ptrHahSymToObjTab = $ptrHahSymTable->{ptrHahSymToObjTab};

+    my $ptrHahLibErr = $ptrHahError->{ptrHahLibErr};

+    my %excludedLib = ();

+    my @refObjList = ();

+    my $count = 0;

+

+    &utlLog(1, "Searching libraries/objects for referred symbols to exclude...\n");

+

+    foreach my $objRef (keys(%{$ptrHahError->{ptrHahLibObjRef}->{$libRef}}))

+    {

+        if (   (not exists($ptrHahRefObjSet->{$libRef}))

+            || (not exists($ptrHahRefObjSet->{$libRef}->{$objRef})))

+        {

+            $ptrHahRefObjSet->{$libRef} = {} if (not exists($ptrHahRefObjSet->{$libRef}));

+            $ptrHahRefObjSet->{$libRef}->{$objRef} = 1;

+        }

+        elsif ($ptrHahRefObjSet->{$libRef}->{$objRef} != 1)

+        {

+            next;

+        }

+        push(@refObjList, $libRef);

+        push(@refObjList, $objRef);

+    }

+    $count = $#refObjList + 1;

+    while ($count > 0)

+    {

+        my $refLib = shift(@refObjList);

+        my $refObj = shift(@refObjList);

+        my @symDef = keys(%{$ptrHahObjToSymTab->{$refLib}->{$refObj}->{+SYM_DEF}});

+

+        &utlLog(2, "Defined symbols in " . $refLib . "(" . $refObj . "): @symDef\n");

+        $ptrHahRefObjSet->{$refLib}->{$refObj} = 2;

+

+        foreach my $symbol (@symDef)

+        {

+            my $ptrHahLibObjSymUndef = $ptrHahSymToObjTab->{$symbol}->{+SYM_UNDEF};

+            my @libList = keys(%$ptrHahLibObjSymUndef);

+

+            foreach my $lib (@libList)

+            {

+                if (not exists($excludedLib{$lib}))

+                {

+                    if (&hasRule($ptrHahRuleGut, $lib, "*"))

+                    {

+                        my @objList = keys(%{$ptrHahLibObjSymUndef->{$lib}});

+

+                        foreach my $obj (@objList)

+                        {

+                            if ((not exists($ptrHahRefObjSet->{$lib})) || (not exists($ptrHahRefObjSet->{$lib}->{$obj})))

+                            {

+                                &utlLog(1, "Using " . $lib . "(" . $obj . ") to exclude objects due to reference to " . $symbol . " in " . $refLib . "(" . $refObj . ")\n");

+                                push(@refObjList, $lib);

+                                push(@refObjList, $obj);

+                                $ptrHahRefObjSet->{$lib} = {} if (not exists($ptrHahRefObjSet->{$lib}));

+                                $ptrHahRefObjSet->{$lib}->{$obj} = 1;

+                            }

+                        }

+                    }

+                    else

+                    {

+                        &utlLog(1, "Adding rule to exclude " . $lib . " due to reference to " . $symbol . " in " . $refLib . "(" . $refObj . ")\n");

+                        &addRule($ptrHahRuleGut, $lib, "*", $hdlFileRule);

+                        &addHint($ptrHahHintGut, $lib, "*", "*", $hdlFileHint);

+                        $ptrHahLibErr->{$lib} = 1;

+                        $excludedLib{$lib} = 1;

+                    }

+                }

+            }

+        }

+        last if (scalar keys(%excludedLib));

+        $count = $#refObjList + 1;

+        &utlLog(2, "Referred objects(" . $count . "): @refObjList\n");

+    }

+}

+

+sub examineLib

+{

+    my ($ptrHahSymTable, $ptrHahRule, $ptrHahHint, $ptrHahError) = @_;

+    my $fileRule = $ptrHahRule->{file};

+    my $ptrHahRuleGut = $ptrHahRule->{ptrHahGut};

+    my $fileHint = $ptrHahHint->{file};

+    my $ptrHahHintGut = $ptrHahHint->{ptrHahGut};

+    my $ptrHahLibSymErr = $ptrHahError->{ptrHahLibSymErr};

+    my $ptrHahLibObjRef = $ptrHahError->{ptrHahLibObjRef};

+    my %refObjSet = ();

+

+    open(FILE_HINT, ">>" . $fileHint) or &utlDie("Cannot open " . $fileHint);

+    open(FILE_RULE, ">>" . $fileRule) or &utlDie("Cannot open " . $fileRule);

+

+    # Handle error symbols.

+    foreach my $lib (keys(%$ptrHahLibSymErr))

+    {

+        &examineLibGut($lib, $ptrHahSymTable, $ptrHahRuleGut, \*FILE_RULE, $ptrHahHintGut, \*FILE_HINT, $ptrHahError);

+    }

+

+    # Handle referred objects.

+    foreach my $lib (keys(%$ptrHahLibObjRef))

+    {

+        &examineLibGutExt($lib, $ptrHahSymTable, $ptrHahRuleGut, \*FILE_RULE, $ptrHahHintGut, \*FILE_HINT, $ptrHahError, \%refObjSet);

+    }

+

+    close(FILE_RULE);

+    close(FILE_HINT);

+}

+

+# -----------------------------------------------------------------------------

+

+sub getDisplayValue

+{

+    my ($formatStr, $value) = @_;

+

+    if ($formatStr =~ /!/)

+    {

+        return "on" if ($value);

+        return "off";

+    }

+    return $value;

+}

+

+sub help

+{

+    my ($ptrHahOptDesc) = @_;

+

+    printf("\nPartial Link Auto-Filter Script (%s) ver. %s\n", basename( __FILE__), $Version);

+    printf("\nOptions:\n\n");

+

+    foreach my $key (sort(keys(%$ptrHahOptDesc)))

+    {

+        printf("%s\n", $ptrHahOptDesc->{$key}->[1]);

+        printf("\t\t%s", $ptrHahOptDesc->{$key}->[2]);

+        printf(" (default: %s)", &getDisplayValue($ptrHahOptDesc->{$key}->[0], $ptrHahOptDesc->{$key}->[3])) if ($ptrHahOptDesc->{$key}->[3] ne "");

+        printf("\n");

+    }

+    printf("\nAn example:\n\n");

+    printf("Command: perl %s --lib-dir=build\\ASTRO36V3_DEMO\\tdd128dpa\\MT6236o\\lib --lib-dir=mtk_lib\\MT6236\\S00\\tdd128dpa\\CMCC_TD0200_SEGE --scatter-file=custom\\system\\ASTRO36V3_DEMO_BB\\scatASTRO36V3_DEMO_FOTA.txt --link-option-file=make\\~sortedLibs.tmp --nm=pcore\\tools\\MinGW\\bin\\arm-none-eabi-nm.exe --readelf=pcore\\tools\\MinGW\\bin\\arm-none-eabi-readelf.exe --strip=pcore\\tools\\MinGW\\bin\\arm-none-eabi-strip.exe\n\n", __FILE__);

+}

+

+use constant {

+    TYPE_AUTO    => 1,

+    TYPE_NORMAL  => 2,

+    TYPE_PARTIAL => 3,

+};

+

+sub getOption

+{

+    my ($ptrHahOpt, $ptrHahOptPath, $ptrHahOptPathMust) = @_;

+    my %optDesc = (

+        "ptrAryLibDir" => [ "lib-dir=s", "--lib-dir=", "a path to a library directory for partial link", "" ],

+        "ptrAryUserExclusionFile" => [ "exclusion-file=s", "--exclusion-file=", "a path to a user exclusion file", "" ],

+        "fileScatter" => [ "scatter-file=s", "--scatter-file=", "a path to a scatter file", "" ],

+        "fileLinkOption" => [ "link-option-file=s", "--link-option-file=", "a path to a file with link options for final link", "" ],

+        "dirInfo" => [ "info-dir=s", "--info-dir=", "a path to a directory to access info", $ptrHahOptPathMust->{dirInfo} ],

+        "dirSave" => [ "save-dir=s", "--save-dir=", "a path to a directory to save log", $ptrHahOptPathMust->{dirSave} ],

+        "dirTemp" => [ "temp-dir=s", "--temp-dir=", "a path to a temp directory", $ptrHahOptPathMust->{dirTemp} . "/pl_temp" ],

+        "cmd_nm" => [ "nm=s", "--nm=", "a path to command nm", "" ],

+        "cmd_readelf" => [ "readelf=s", "--readelf=", "a path to command readelf", "" ],

+        "cmd_strip" => [ "strip=s", "--strip=", "a path to command strip", "" ],

+        "cmd_armar" => [ "armar=s", "--armar=", "a path to command armar", $Cmd_armar ],

+        "cmd_armlink" => [ "armlink=s", "--armlink=", "a path to command armlink", $Cmd_armlink ],

+        "fileLinkErr" => [ "link-err-file=s", "--link-err-file=", "a path to a file to output link error", "" ],

+        "reGenRule" => [ "regen-rule!", "--[no-]regen-rule", "re-generate rule", $ptrHahOpt->{reGenRule} ],

+        "reGenHint" => [ "regen-hint!", "--[no-]regen-hint", "re-generate hint", $ptrHahOpt->{reGenHint} ],

+        "hintToRule" => [ "hint-to-rule!", "--[no-]hint-to-rule", "convert hint to rule", $ptrHahOpt->{hintToRule} ],

+        "excludeCppLib" => [ "exclude-cpp-lib!", "--[no-]exclude-cpp-lib", "exclude all C++ libraries when errors happen", $ptrHahOpt->{excludeCppLib} ],

+        "passLimit" => [ "pass-limit=i", "--pass-limit=", "set limitation of the max. pass to resolve linking error", $ptrHahOpt->{passLimit} ],

+        "simulate" => [ "simulate!", "--[no-]simulate", "run the whole process without modifying libraries", $ptrHahOpt->{simulate} ],

+        "linkType" => [ "link-type=i", "--link-type=", "specify 1 for \"auto enabling partial link\", 2 for \"normal link\", or 3 for \"partial link\"", $ptrHahOpt->{linkType} ],

+        "libSizeInfo" => [ "lib-size-info!", "--[no-]lib-size-info", "generate library size info", $ptrHahOpt->{libSizeInfo} ],

+        "stripDebugInfo" => [ "strip-debug-info!", "--[no-]strip-debug-info", "strip debug info", $ptrHahOpt->{stripDebugInfo} ],

+        "stripDebugCount" => [ "strip-debug-count=i", "--strip-debug-count=", "set default count to strip debug info if \"pl_debug.txt\" is not available", $ptrHahOpt->{stripDebugCount} ],

+        "check" => [ "check!", "--[no-]check", "not to run if partial link is done last time", $ptrHahOpt->{check} ],

+        "forcePartialLink" => [ "force-partial-link!", "--[no-]force-partial-link", "force to run partial link only", $ptrHahOpt->{forcePartialLink} ],

+        "update" => [ "update!", "--[no-]update", "update libraries specified in \"--lib-dir=...\" with partially-linked ones", $ptrHahOpt->{update} ],

+        "save" => [ "save!", "--[no-]save", "save partially-linked libraries", $ptrHahOpt->{save} ],

+        "dbgLevel" => [ "debug-level=i", "--debug-level=", "set debug level", $DbgLevel ],

+        "infoLevel" => [ "info-level=i", "--info-level=", "set how much information displays in the screen", $InfoLevel ],

+        "help" => [ "help", "--help", "show this help", "" ],

+    );

+

+    if (!GetOptions(

+        $optDesc{ptrAryLibDir}[0] => \@{$ptrHahOptPathMust->{ptrAryLibDir}},

+        $optDesc{ptrAryUserExclusionFile}[0] => \@{$ptrHahOptPath->{ptrAryUserExclusionFile}},

+        $optDesc{fileScatter}[0] => \$ptrHahOptPathMust->{fileScatter},

+        $optDesc{fileLinkOption}[0] => \$ptrHahOptPathMust->{fileLinkOption},

+        $optDesc{dirInfo}[0] => \$ptrHahOptPathMust->{dirInfo},

+        $optDesc{dirSave}[0] => \$ptrHahOptPathMust->{dirSave},

+        $optDesc{dirTemp}[0] => \$ptrHahOptPathMust->{dirTemp},

+        $optDesc{cmd_nm}[0] => \$ptrHahOptPathMust->{cmd_nm},

+        $optDesc{cmd_readelf}[0] => \$ptrHahOptPathMust->{cmd_readelf},

+        $optDesc{cmd_strip}[0] => \$ptrHahOptPathMust->{cmd_strip},

+        $optDesc{cmd_armar}[0] => \$ptrHahOptPath->{cmd_armar},

+        $optDesc{cmd_armlink}[0] => \$ptrHahOptPath->{cmd_armlink},

+        $optDesc{fileLinkErr}[0] => \$ptrHahOptPath->{fileLinkErr},

+        $optDesc{reGenRule}[0] => \$ptrHahOpt->{reGenRule},

+        $optDesc{reGenHint}[0] => \$ptrHahOpt->{reGenHint},

+        $optDesc{hintToRule}[0] => \$ptrHahOpt->{hintToRule},

+        $optDesc{excludeCppLib}[0] => \$ptrHahOpt->{excludeCppLib},

+        $optDesc{passLimit}[0] => \$ptrHahOpt->{passLimit},

+        $optDesc{simulate}[0] => \$ptrHahOpt->{simulate},

+        $optDesc{linkType}[0] => \$ptrHahOpt->{linkType},

+        $optDesc{libSizeInfo}[0] => \$ptrHahOpt->{libSizeInfo},

+        $optDesc{stripDebugInfo}[0] => \$ptrHahOpt->{stripDebugInfo},

+        $optDesc{stripDebugCount}[0] => \$ptrHahOpt->{stripDebugCount},

+        $optDesc{check}[0] => \$ptrHahOpt->{check},

+        $optDesc{forcePartialLink}[0] => \$ptrHahOpt->{forcePartialLink},

+        $optDesc{update}[0] => \$ptrHahOpt->{update},

+        $optDesc{save}[0] => \$ptrHahOpt->{save},

+        $optDesc{dbgLevel}[0] => \$DbgLevel,

+        $optDesc{infoLevel}[0] => \$InfoLevel,

+        $optDesc{help}[0] => \$ptrHahOpt->{help}) || $ptrHahOpt->{help})

+    {

+        &help(\%optDesc);

+        return 1;

+    }

+

+    foreach my $key (keys(%$ptrHahOptPath))

+    {

+        if (ref($ptrHahOptPath->{$key}) eq 'ARRAY')

+        {

+            for (my $i = 0; $i <= $#{$ptrHahOptPath->{$key}}; $i++)

+            {

+                $ptrHahOptPath->{$key}->[$i] = &utlTrim($ptrHahOptPath->{$key}->[$i]);

+                &utlDie("Value of option " . $optDesc{$key}[1] . " cannot be empty") if ($ptrHahOptPath->{$key}->[$i] eq "");

+                $ptrHahOptPath->{$key}->[$i] = &utlAbsPath($ptrHahOptPath->{$key}->[$i]);

+            }

+        }

+        else

+        {

+            $ptrHahOptPath->{$key} = &utlTrim($ptrHahOptPath->{$key});

+            if ($ptrHahOptPath->{$key} ne "")

+            {

+                if (($key eq "fileLinkErr") && (! -e $ptrHahOptPath->{$key}))

+                {

+                    my $path = dirname($ptrHahOptPath->{$key});

+                    my $fileName = basename($ptrHahOptPath->{$key});

+

+                    &utlDie("Cannot find " . $path) unless (-e $path);

+                    $ptrHahOptPath->{$key} = &utlAbsPath($path) . "/" . $fileName;

+                }

+                else

+                {

+                    $ptrHahOptPath->{$key} = &utlAbsPath($ptrHahOptPath->{$key});

+                }

+            }

+            elsif ($optDesc{$key}->[3] ne "")

+            {

+                $ptrHahOptPath->{$key} = $optDesc{$key}->[3];

+            }

+        }

+    }

+    foreach my $key (keys(%$ptrHahOptPathMust))

+    {

+        if (ref($ptrHahOptPathMust->{$key}) eq 'ARRAY')

+        {

+            my $ptrAryPath = [];

+

+            &utlDie("Must specify " . $optDesc{$key}[1]) if ($#{$ptrHahOptPathMust->{$key}} < 0);

+            for (my $i = 0; $i <= $#{$ptrHahOptPathMust->{$key}}; $i++)

+            {

+                my $path = $ptrHahOptPathMust->{$key}->[$i];

+

+                $path = &utlTrim($path);

+                &utlDie("Value of option " . $optDesc{$key}[1] . " cannot be empty") if ($path eq "");

+                if (-e $path)

+                {

+                    push(@$ptrAryPath, &utlAbsPath($path));

+                }

+                else

+                {

+                    &utlLog(0, "Ignore " . $path . " due to no existence\n");

+                }

+            }

+            &utlDie("No valid path is specified by option " . $optDesc{$key}[1]) unless ($#$ptrAryPath >= 0);

+            $ptrHahOptPathMust->{$key} = $ptrAryPath;

+        }

+        else

+        {

+            $ptrHahOptPathMust->{$key} = &utlTrim($ptrHahOptPathMust->{$key});

+            &utlDie("Value of option " . $optDesc{$key}[1] . " cannot be empty") if ($ptrHahOptPathMust->{$key} eq "");

+            $ptrHahOptPathMust->{$key} = &utlAbsPath($ptrHahOptPathMust->{$key});

+        }

+    }

+

+    if (   ($ptrHahOpt->{linkType} != TYPE_AUTO)

+        && ($ptrHahOpt->{linkType} != TYPE_NORMAL)

+        && ($ptrHahOpt->{linkType} != TYPE_PARTIAL))

+    {

+        &utlDie("Illegal value " . $ptrHahOpt->{linkType} . " for option " . $optDesc{linkType}[1]);

+    }

+    return 0;

+}

+

+sub formatCmd

+{

+    my ($cmd) = @_;

+

+    $cmd =~ s/\(/"("/g;

+    $cmd =~ s/\)/")"/g;

+

+    return $cmd;

+}

+

+sub dumpOpt

+{

+    my ($ptrHah) = @_;

+

+    foreach my $key (keys(%$ptrHah))

+    {

+        &utlLog(1, "-> " . $key . "\n");

+        &utlDump(1, $ptrHah->{$key});

+    }

+}

+

+BEGIN

+{

+    my $_file = "";

+

+    sub setupErrorHandling

+    {

+        my ($file) = @_;

+

+        &utlRmFile(dirname($file) . "/" . basename($FileErr));

+        $_file = $file;

+    }

+

+    sub errorHandling

+    {

+        if ($_file ne "")

+        {

+            &utlCpFile($FileErr, $_file);

+            &utlCpFile($FileErr, dirname($_file));

+        }

+    }

+}

+

+sub readIniFile

+{

+    my ($ptrHahIni) = @_;

+    my $fileIni = $ptrHahIni->{file};

+    my $ptrHahAttr = $ptrHahIni->{ptrHahAttr};

+    my $ptrHahValue = $ptrHahIni->{ptrHahValue};

+    my $ptrHahOnOff = $ptrHahIni->{ptrHahOnOff};

+

+    if (-e $fileIni)

+    {

+        open(FILE_INI, "<" . $fileIni) or &utlDie("Cannot open " . $fileIni);

+        

+        foreach my $line (<FILE_INI>)

+        {

+            next if (($line =~ /^\s*$/) || ($line =~ /^\s*;/));

+

+            # key = value

+            if ($line =~ /^\s*(\S+)\s*=\s*([-]?\d+)/)

+            {

+                my $key = $1;

+                my $value = $2;

+

+                if (exists($ptrHahAttr->{$key}))

+                {

+                    $ptrHahAttr->{$key} = $value;

+                }

+                elsif (exists($ptrHahValue->{$key}))

+                {

+                    $ptrHahValue->{$key}->[0] = 1;

+                    ${$ptrHahValue->{$key}->[1]} = $value;

+

+                    if ($#{$ptrHahValue->{$key}} > 1)

+                    {

+                        my $found = 0;

+

+                        for (my $i = 2; $i <= $#{$ptrHahValue->{$key}}; $i++)

+                        {

+                            if ($value == $ptrHahValue->{$key}->[$i])

+                            {

+                                $found = 1;

+                                last;

+                            }

+                        }

+                        &utlDie("Invalid value \"" . $value . "\" for " . $key . " in " . $fileIni) unless ($found);

+                    }

+                }

+                elsif (exists($ptrHahOnOff->{$key}))

+                {

+                    if (($value == 1) || ($value == 0))

+                    {

+                        $ptrHahOnOff->{$key}->[0] = 1;

+                        ${$ptrHahOnOff->{$key}->[1]} = $value;

+                    }

+                    else

+                    {

+                        &utlDie("Invalid value \"" . $value . "\" for " . $key . " in " . $fileIni . "; should be 0 for disable and 1 for enable");

+                    }

+                }

+                else

+                {

+                    &utlDie("Invalid key \"" . $key . "\" in " . $fileIni);

+                }

+            }

+            else

+            {

+                &utlDie("Unrecognized line \"" . $line . "\" in " . $fileIni);

+            }

+        }

+

+        close(FILE_INI);

+    }

+}

+

+sub writeIniFile

+{

+    my ($ptrHahIni) = @_;

+    my $fileIni = $ptrHahIni->{file};

+    my $flag = $ptrHahIni->{flag};

+    my $ptrHahAttr = $ptrHahIni->{ptrHahAttr};

+    my $ptrHahValue = $ptrHahIni->{ptrHahValue};

+    my $ptrHahOnOff = $ptrHahIni->{ptrHahOnOff};

+

+    if ($flag != 0)

+    {

+        open(FILE_INI, ">" . $fileIni) or &utlDie("Cannot open " . $fileIni);

+

+        print(FILE_INI "\n; DO NOT MODIFY THE FOLLOWING INFORMATION\n\n");

+

+        foreach my $key (keys(%$ptrHahAttr))

+        {

+            print(FILE_INI $key . "=" . $ptrHahAttr->{$key} . "\n");

+        }

+

+        print(FILE_INI "\n; UNCOMMENT THE FOLLOWING TO OVERRIDE CORRESPONDING SETTINGS BY REMOVING \";\"\n\n");

+

+        foreach my $key (keys(%$ptrHahValue))

+        {

+            if ($ptrHahValue->{$key}->[0] != 0)

+            {

+                print(FILE_INI $key . "=" . ${$ptrHahValue->{$key}->[1]} . "\n");

+            }

+            else

+            {

+                print(FILE_INI "; " . $key . "=" . ${$ptrHahValue->{$key}->[1]} . "\n");

+            }

+        }

+        foreach my $key (keys(%$ptrHahOnOff))

+        {

+            if ($ptrHahOnOff->{$key}->[0] != 0)

+            {

+                print(FILE_INI $key . "=" . ${$ptrHahOnOff->{$key}->[1]} . "\n");

+            }

+            elsif (${$ptrHahOnOff->{$key}->[1]})

+            {

+                print(FILE_INI "; " . $key . "=0\n");

+            }

+            else

+            {

+                print(FILE_INI "; " . $key . "=1\n");

+            }

+        }

+

+        close(FILE_INI);

+    }

+}

+

+sub setIni

+{

+    my ($ptrHahIni, $key, $value) = @_;

+

+    $ptrHahIni->{ptrHahAttr}->{$key} = $value;

+    $ptrHahIni->{flag} = 1;

+}

+

+sub syncLib

+{

+    my ($ptrHahLib) = @_;

+    my $ptrAryLibDir = $ptrHahLib->{ptrAryLibDir};

+    my $suffix = $ptrHahLib->{suffix};

+    my $suffix_bak = $ptrHahLib->{suffix_bak};

+

+    foreach my $dirSrcLib (@$ptrAryLibDir)

+    {

+        my $dirDstLib = $dirSrcLib . $suffix;

+        my $dirSrcBak = $dirSrcLib . $suffix_bak;

+

+        if ((-e $dirSrcBak) && (-e $dirDstLib))

+        {

+            my @libList = glob($dirSrcLib . "/*");

+

+            foreach my $lib (@libList)

+            {

+                if (-f $lib)

+                {

+                    my $libName = basename($lib);

+                    my $timestamp = &utlGetTimestamp($lib);

+                    my $libDst = $dirDstLib . "/" . $libName;

+

+                    if ((-e $libDst) && ($timestamp == &utlGetTimestamp($libDst)))

+                    {

+                        my $libSrcBak = $dirSrcBak . "/" . $libName;

+

+                        if (-e $libSrcBak)

+                        {

+                            &utlLog(1, "Restoring " . $libName . "\n");

+                            &utlCpFile($libSrcBak, $lib) if ($timestamp != &utlGetTimestamp($libSrcBak));

+                        }

+                    }

+                }

+            }

+        }

+        &utlRmTree($dirSrcBak);

+        &utlRmTree($dirDstLib);

+        &utlRmTree($dirSrcLib . $ptrHahLib->{suffix_dbg});

+        &utlRmTree($dirSrcLib . $ptrHahLib->{suffix_save});

+    }

+}

+

+sub checkLib

+{

+    my ($ptrHahLib) = @_;

+    my $ptrAryLibDir = $ptrHahLib->{ptrAryLibDir};

+    my $fileTimestamp = $ptrHahLib->{fileTimestamp};

+    my $ptrHahLibNew = $ptrHahLib->{ptrHahLibNew};

+    my $ptrHahLibUpdate = $ptrHahLib->{ptrHahLibUpdate};

+    my %timestamp = ();

+

+    # Load timestamp of each library.

+    if (-e $fileTimestamp)

+    {

+        open(FILE_TS, "<" . $fileTimestamp) or &utlDie("Cannot open " . $fileTimestamp);

+

+        foreach my $line (<FILE_TS>)

+        {

+            if ($line =~ /^\s*(\S+)\s*(\S+)/)

+            {

+                $timestamp{$1} = $2;

+            }

+        }

+

+        close(FILE_TS);

+    }

+

+    foreach my $dirSrcLib (@$ptrAryLibDir)

+    {

+        my @libList = glob($dirSrcLib . "/*.lib");

+

+        foreach my $lib (@libList)

+        {

+            my $curTimestamp = (stat($lib))[9];

+            my $libName = basename($lib);

+

+            next if (exists($IgnoredLib{$libName}));

+

+            if (not exists($timestamp{$libName}))

+            {

+                $ptrHahLibNew->{$libName} = 1;

+            }

+            elsif ($curTimestamp > $timestamp{$libName})

+            {

+                $ptrHahLibUpdate->{$libName} = 1;

+            }

+        }

+    }

+}

+

+sub generateLibSize

+{

+    my ($ptrHahLib) = @_;

+    my $ptrAryLibDir = $ptrHahLib->{ptrAryLibDir};

+    my $ptrHahLibNew = $ptrHahLib->{ptrHahLibNew};

+    my $ptrHahLibUpdate = $ptrHahLib->{ptrHahLibUpdate};

+    my $dirLibSize = $ptrHahLib->{dirLibSize};

+

+    &utlMkDir($dirLibSize);

+

+    foreach my $dirSrcLib (@$ptrAryLibDir)

+    {

+        my @libList = glob($dirSrcLib . "/*.lib");

+

+        foreach my $lib (@libList)

+        {

+            my $libName = basename($lib);

+

+            if (exists($ptrHahLibNew->{$libName}) || exists($ptrHahLibUpdate->{$libName}))

+            {

+                &utlLog(1, "Generating size info for " . $libName . "\n");

+                &utlSystem($Cmd_armar . " --sizes " . $lib . " >" . $dirLibSize . "/" . $libName . ".txt");

+            }

+        }

+    }

+}

+

+sub normalLinkStrip

+{

+    my ($ptrHanLib, $ptrHahError, $fileCppLib, $fileDbgLib, $fileLinkOption, $stripDebugCount) = @_;

+    my $ret = 0;

+

+    &copyLib($ptrHanLib->{ptrAryLibDir}, "", $ptrHanLib->{suffix});

+    &removeDebugInfo($ptrHanLib, $ptrHanLib->{suffix}, $fileCppLib, $fileDbgLib, $stripDebugCount);

+    $ret = &finalLink("", $fileLinkOption, $ptrHahError);

+    &removeDstLibDir($ptrHanLib, $ptrHanLib->{suffix}, "") if ($ret == LINK_OK);

+    return $ret;

+}

+

+sub partialLink

+{

+    my ($ptrHahOpt, $ptrHahOptPath, $ptrHahOptPathMust, $ptrHahLib, $ptrHahError, $fileCppLib, $fileDbgLib, $fileLinkOption) = @_;

+    my %rule = (

+        "file" => $ptrHahOptPathMust->{dirSave} . "/pl.rule",

+        "fileRule" => $ptrHahOptPathMust->{dirInfo} . "/pl_rule.txt",

+        "ptrHahGut" => {},

+    );

+    my %hint = (

+        "file" => $ptrHahOptPathMust->{dirSave} . "/pl.hint",

+        "fileHint" => $ptrHahOptPathMust->{dirInfo} . "/pl_hint.txt",

+        "toRule" => $ptrHahOpt->{hintToRule},

+        "ptrHahGut" => {},

+        "use" => 1,

+    );

+    my $fileExclusion = $ptrHahOptPathMust->{dirInfo} . "/pl_exclusion.txt";

+    my $pass = 0;

+    my $ret = 1;

+

+    # Remove temporary files.

+    &utlRmFile($rule{file});

+    &utlRmFile($hint{file});

+

+    # Create temporary directories for partially-linked libraries.

+    &createDstLibDir($ptrHahLib);

+

+    if (($ptrHahOpt->{reGenRule} == 0) && (-e $rule{fileRule}))

+    {

+        &readRuleFile(\%rule);

+    }

+    else

+    {

+        # Parse scatter file and output rules to the rule file.

+        &parseScatterFile($ptrHahOptPathMust->{fileScatter}, \%rule);

+

+        # Read user exclusion file and output rules to the rule file.

+        &readUserExclusionFile($ptrHahOptPath->{ptrAryUserExclusionFile}, \%rule);

+

+        # Read exclusion file and output rules to the rule file.

+        &readExclusionFile($fileExclusion, \%rule) if (-e $fileExclusion);

+

+        # Write built-in excluded libraries to the rule file.

+        &writeExcludedLib(\@ExcludedLib, \%rule);

+    }

+

+    # Read hint file and output rules to the rule file.

+    &readHintFile(\%hint, \%rule) if (($ptrHahOpt->{reGenHint} == 0) && (-e $hint{fileHint}));

+

+    # Generate a link option file.

+    &generateLinkOptFile($ptrHahLib, $rule{ptrHahGut}, $ptrHahOptPathMust->{fileLinkOption}, $fileLinkOption);

+

+    &utlLog(2, "Dumping hint...\n");

+    &utlDump(2, $hint{ptrHahGut});

+    &utlLog(2, "Dumping rule...\n");

+    &utlDump(2, $rule{ptrHahGut});

+

+    while ($pass < $ptrHahOpt->{passLimit})

+    {

+        $pass++;

+        &utlLog(1, "Pass " . $pass . "...\n");

+

+        # Set a file to output linking error.

+        $ptrHahError->{file} = $ptrHahOptPathMust->{dirSave} . "/pl_error_" . $pass . ".log";

+

+        if ($pass != 1)

+        {

+            &loadSymTable($ptrHahLib, $fileCppLib);

+            &ruleOutObjWithRedefinedSym($ptrHahLib->{ptrHahSymTable}, \%rule, \%hint, $ptrHahError->{ptrHahLibErr});

+

+            # Find out libraries where objects with error symbols are located.

+            &utlLog(2, "Dumping errors from objects with symbols...\n");

+            &utlDump(2, $ptrHahError->{ptrHahObjSymErr});

+            &handleObjSymErr($ptrHahLib->{ptrHahSymTable}, \%rule, \%hint, $ptrHahError, $ptrHahOpt->{excludeCppLib});

+

+            &utlLog(2, "Dumping errors from libraries with symbols...\n");

+            &utlDump(2, $ptrHahError->{ptrHahLibSymErr});

+            &examineLib($ptrHahLib->{ptrHahSymTable}, \%rule, \%hint, $ptrHahError);

+

+            last unless (scalar keys(%{$ptrHahError->{ptrHahLibErr}}));

+            &utlLog(3, "Dumping rule...\n");

+            &utlDump(3, $rule{ptrHahGut});

+        }

+

+        # Partially link objects in the same libraries according to rule and linking error.

+        $Current_time = time;

+        system("echo T_S,partialLinkLib,P,$Current_time >> $ptrHahOptPathMust->{dirSave}/PlinkTime.log");

+        &partialLinkLib($ptrHahLib, \%rule, \%hint, $ptrHahError->{ptrHahLibErr});

+        $Current_time = time;

+        system("echo T_E,partialLinkLib,P,$Current_time >> $ptrHahOptPathMust->{dirSave}/PlinkTime.log");

+

+        # Link all by using command from a file specified in option.

+        $ret = &finalLink("--mangled", $fileLinkOption, $ptrHahError);

+        last if (($ret == LINK_OK) || ($ret == LINK_OUT_OF_MEM) || ($ret == LINK_UNKNOWN));

+    }

+    if ($ret == LINK_OUT_OF_MEM)

+    {

+        if ($ptrHahOpt->{stripDebugInfo})

+        {

+            &utlLog(0, "Trying link with debug info stripping due to \"Fatal error: L6000U: Out of memory.\"\n");

+            $ptrHahError->{file} = $ptrHahOptPathMust->{dirSave} . "/pl_error_dbg.log";

+            &copyLib($ptrHahLib->{ptrAryLibDir}, $ptrHahLib->{suffix}, $ptrHahLib->{suffix_dbg});

+            &removeDebugInfo($ptrHahLib, $ptrHahLib->{suffix}, $fileCppLib, $fileDbgLib, $ptrHahOpt->{stripDebugCount});

+            $ret = &finalLink("", $fileLinkOption, $ptrHahError);

+            $ret = LINK_OK_STRIP if ($ret == LINK_OK);

+            if (($ret == LINK_OK_STRIP) || ($ret == LINK_OUT_OF_MEM))

+            {

+                &removeDstLibDir($ptrHahLib, $ptrHahLib->{suffix}, "");

+                $ptrHahLib->{suffix} = $ptrHahLib->{suffix_dbg};

+            }

+        }

+    }

+    if (($ret == LINK_OK) || ($ret == LINK_OK_STRIP) || ($ret == LINK_OUT_OF_MEM))

+    {

+        if ($ptrHahOpt->{simulate})

+        {

+            &removeDstLibDir($ptrHahLib, $ptrHahLib->{suffix}, $ptrHahLib->{suffix_save});

+        }

+        elsif ($ptrHahOpt->{update} == 0)

+        {

+            my $suffix = "";

+

+            $suffix = $ptrHahLib->{suffix_save} if ($ptrHahOpt->{save});

+            &removeDstLibDir($ptrHahLib, $ptrHahLib->{suffix}, $suffix);

+            &utlCpFile($rule{file}, $rule{fileRule});

+            &utlCpFile($hint{file}, $hint{fileHint});

+        }

+        else

+        {

+            &finalizeLib($ptrHahLib);

+            &utlCpFile($rule{file}, $rule{fileRule});

+            &utlCpFile($hint{file}, $hint{fileHint});

+        }

+    }

+    elsif ($ret != LINK_OUT_OF_MEM)

+    {

+        &removeDstLibDir($ptrHahLib, $ptrHahLib->{suffix}, "");

+        &removeDstLibDir($ptrHahLib, $ptrHahLib->{suffix_dbg}, "");

+    }

+    return $ret;

+}

+

+sub generateLibTimestamp

+{

+    my ($ptrHahLib) = @_;

+    my $ptrAryLibDir = $ptrHahLib->{ptrAryLibDir};

+    my $fileTimestamp = $ptrHahLib->{fileTimestamp};

+

+    &utlLog(1, "Generating library timestamp\n");

+    open(FILE_TS, ">" . $fileTimestamp) or &utlDie("Cannot open " . $fileTimestamp);

+

+    foreach my $dirSrcLib (@$ptrAryLibDir)

+    {

+        my @libList = glob($dirSrcLib . "/*.lib");

+

+        foreach my $lib (@libList)

+        {

+            my $timestamp = (stat($lib))[9];

+            my $libName = basename($lib);

+

+            print(FILE_TS $libName . " " . $timestamp . "\n");

+        }

+    }

+

+    close(FILE_TS);

+}

+

+sub main

+{  

+	  $Current_time = time;

+    my ($argc, $argv) = @_;

+    # Set options.

+    my %opt = (

+        "reGenRule" => 1,

+        "reGenHint" => 0,

+        "hintToRule" => 1,

+        "excludeCppLib" => 0,

+        "passLimit" => 7,

+        "simulate" => 0,

+        "linkType" => TYPE_AUTO,

+        "libSizeInfo" => 1,

+        "stripDebugInfo" => 1,

+        "stripDebugCount" => 8,

+        "check" => 0,

+        "forcePartialLink" => 0,

+        "update" => 1,

+        "save" => 0,

+        "help" => 0,

+    );

+    # Set options with validated path values.

+    my %optPath = (

+        "ptrAryUserExclusionFile" => [],

+        "cmd_armar" => "",

+        "cmd_armlink" => "",

+        "fileLinkErr" => "",

+    );

+    my %optPathMust = (

+        "ptrAryLibDir" => [],

+        "cmd_nm" => "",

+        "cmd_readelf" => "",

+        "cmd_strip" => "",

+        "fileScatter" => "",

+        "fileLinkOption" => "",

+        "dirInfo" => cwd(),

+        "dirSave" => cwd(),

+        "dirTemp" => cwd(),

+    );

+    my $plink_start = time;

+    my $plink_end = "";

+

+    return 2 if (&getOption(\%opt, \%optPath, \%optPathMust));

+    system("echo T_S,Plink,P,$Current_time > $optPathMust{dirSave}/PlinkTime.log");

+

+    # Set global variables.

+    $FileLog = $optPathMust{dirSave} . "/pl.log";

+    system("date/T >" . $FileLog);

+    system("time/T >>" . $FileLog);

+    $FileErr = $optPathMust{dirSave} . "/pl.err";

+    $FileTmp = $optPathMust{dirSave} . "/pl.tmp";

+    $DirTemp = $optPathMust{dirTemp} . "/pl_temp";

+    $Cmd_nm = &formatCmd($optPathMust{cmd_nm});

+    $Cmd_readelf = &formatCmd($optPathMust{cmd_readelf});

+    $Cmd_strip = &formatCmd($optPathMust{cmd_strip});

+    $Cmd_armar = &formatCmd($optPath{cmd_armar});

+    $Cmd_armlink = &formatCmd($optPath{cmd_armlink});

+

+    # Show command line.

+    &utlLog(1, "[@$argv]\n");

+

+    &utlLog(1, "Dumping options...\n");

+    &dumpOpt(\%opt);

+    &dumpOpt(\%optPath);

+    &dumpOpt(\%optPathMust);

+

+    use constant {

+        STATE_NONE              => 0,

+        STATE_NORMAL_LINK       => 1,

+        STATE_NORMAL_LINK_STRIP => 2,

+        STATE_PARTIAL_LINK      => 3,

+    };

+    my %ini = (

+        "file" => $optPathMust{dirInfo} . "/pl.ini",

+        "flag" => 0,

+        "ptrHahAttr" => {

+            "state" => STATE_NONE,

+            "link" => LINK_NONE,

+        },

+        "ptrHahValue" => {

+            "pass_limit" => [ 0, \$opt{passLimit} ],

+            "link_type" => [ 0, \$opt{linkType}, TYPE_AUTO, TYPE_NORMAL, TYPE_PARTIAL ],

+            "strip_debug_count" => [ 0, \$opt{stripDebugCount} ],

+        },

+        "ptrHahOnOff" => {

+            "regen_hint" => [ 0, \$opt{reGenHint} ],

+            "exclude_cpp_lib" => [ 0, \$opt{excludeCppLib} ],

+            "simulate" => [ 0, \$opt{simulate} ],

+            "strip_debug_info" => [ 0, \$opt{stripDebugInfo} ],

+            "check" => [ 0, \$opt{check} ],

+            "force_partial_link" => [ 0, \$opt{forcePartialLink} ],

+            "update" => [ 0, \$opt{update} ],

+            "save" => [ 0, \$opt{save} ],

+        },

+    );

+    my %lib = (

+        "ptrAryLibDir" => $optPathMust{ptrAryLibDir},

+        "suffix" => "__PARTIAL_LINK_TEMPORARY",

+        "suffix_bak" => "__PARTIAL_LINK_SRC_BACKUP",

+        "suffix_dbg" => "__PARTIAL_LINK_DBG",

+        "suffix_save" => "__PARTIAL_LINK",

+        "fileTimestamp" => $optPathMust{dirInfo} . "/pl_timestamp.txt",

+        "dirLibSize" => $optPathMust{dirInfo} . "/pl_lib_size",

+        "ptrHahLibNew" => {},

+        "ptrHahLibUpdate" => {},

+        "ptrHahLibUsage" => {},

+        "ptrHahNoDbgLib" => {},

+        "ptrHahSymTable" => {},

+    );

+    my %error = (

+        "file" => "",

+        "fileOutput" => $optPath{fileLinkErr},

+        "ptrHahObjSymErr" => 0,

+        "ptrHahLibSymErr" => 0,

+        "ptrHahLibObjRef" => 0,

+        "ptrHahLibErr" => 0,

+    );

+    my $fileCppLib = $optPathMust{dirInfo} . "/pl_cpp.txt";

+    my $fileDbgLib = $optPathMust{dirInfo} . "/pl_debug.txt";

+    my $fileLinkOption = $optPathMust{dirSave} . "/pl_link_opt.via";

+    my $flagNormalLink = 0;

+    my $flagPartialLink = 0;

+    my $state = STATE_PARTIAL_LINK;

+    my $link = LINK_UNKNOWN;

+    my $ret = 1;

+

+    # Register an error handling.

+    &setupErrorHandling($error{fileOutput});

+    &sysDieRegister(\&errorHandling);

+

+    &utlRmFile($FileErr);

+    &utlRmFile($FileTmp);

+    &utlRmTree($DirTemp);

+

+    &readIniFile(\%ini);

+    &syncLib(\%lib);

+    &checkLib(\%lib);

+    &generateLibSize(\%lib) if ($opt{libSizeInfo});

+

+    &dumpOpt(\%ini);

+    &dumpOpt(\%lib);

+

+    if (   ($opt{check} != 0)

+        # Libraries are not updated.

+        && (scalar keys(%{$lib{ptrHahLibNew}}) == 0)

+        && (scalar keys(%{$lib{ptrHahLibUpdate}}) == 0)

+        # Libraries were partially linked successfully last time.

+        && ($ini{ptrHahAttr}->{state} == STATE_PARTIAL_LINK)

+        && (   ($ini{ptrHahAttr}->{link} == LINK_OK)

+            || ($ini{ptrHahAttr}->{link} == LINK_OK_STRIP)))

+    {

+        &utlLog(0, "Partial link is done last time\n");

+        return 0;

+    }

+

+    # Decide what to do according to result from last time.

+    if (   ($opt{forcePartialLink} == 0)

+        # Stripping debug info is enabled.

+        && ($opt{stripDebugInfo} != 0)

+        # Libraries are not updated.

+        && (scalar keys(%{$lib{ptrHahLibNew}}) == 0)

+        && (scalar keys(%{$lib{ptrHahLibUpdate}}) == 0)

+        # Libraries were stripped successfully last time.

+        && (   (   ($ini{ptrHahAttr}->{state} == STATE_NORMAL_LINK_STRIP)

+                && (   ($ini{ptrHahAttr}->{link} == LINK_OK)

+                    || ($ini{ptrHahAttr}->{link} == LINK_OUT_OF_MEM)))

+            || (   ($ini{ptrHahAttr}->{state} == STATE_PARTIAL_LINK)

+                && (   ($ini{ptrHahAttr}->{link} == LINK_OK_STRIP)

+                    || ($ini{ptrHahAttr}->{link} == LINK_OUT_OF_MEM)))))

+    {

+        $state = STATE_NORMAL_LINK_STRIP;

+    }

+    elsif (   ($opt{forcePartialLink} != 0)

+           || ($opt{linkType} == TYPE_PARTIAL))

+    {

+        $state = STATE_PARTIAL_LINK;

+    }

+    else

+    {

+        $state = STATE_NORMAL_LINK;

+    }

+

+    while (1)

+    {

+        my $curState = $state;

+

+        if ($curState == STATE_NORMAL_LINK)

+        {

+            &utlLog(0, "Trying normal link...\n");

+            $flagNormalLink = 1;

+            $error{file} = $optPathMust{dirSave} . "/pl_error_normal.log";

+            $link = &finalLink("", $optPathMust{fileLinkOption}, \%error);

+            if ($link == LINK_OUT_OF_MEM)

+            {

+                if ($opt{linkType} == TYPE_AUTO)

+                {

+                    $state = STATE_PARTIAL_LINK if ($flagPartialLink == 0);

+                }

+                elsif ($opt{linkType} == TYPE_NORMAL)

+                {

+                    $state = STATE_NORMAL_LINK_STRIP if ($opt{stripDebugInfo});

+                }

+            }

+        }

+        elsif ($curState == STATE_NORMAL_LINK_STRIP)

+        {

+            &utlLog(0, "Trying normal link with debug info stripping...\n");

+            &generateLinkOptFile(\%lib, 0, $optPathMust{fileLinkOption}, $fileLinkOption);

+            $error{file} = $optPathMust{dirSave} . "/pl_error_dbg.log";

+            $link = &normalLinkStrip(\%lib, \%error, $fileCppLib, $fileDbgLib, $fileLinkOption, $opt{stripDebugCount});

+        }

+        elsif ($curState == STATE_PARTIAL_LINK)

+        {

+            &utlLog(0, "Trying partial link...\n");

+            $flagPartialLink = 1;

+            $link = &partialLink(\%opt, \%optPath, \%optPathMust, \%lib, \%error, $fileCppLib, $fileDbgLib, $fileLinkOption);

+            $state = STATE_NORMAL_LINK if (($opt{forcePartialLink} == 0) && ($flagNormalLink == 0) && ($link != LINK_OK) && ($link != LINK_OK_STRIP) && ($link != LINK_OUT_OF_MEM));

+        }

+        if (($link == LINK_OK) || ($link == LINK_OK_STRIP))

+        {

+            &utlLog(0, "Succeed to link\n");

+            &generateLibTimestamp(\%lib) unless ($opt{simulate});

+            $ret = 0;

+        }

+        elsif ($link == LINK_OUT_OF_MEM)

+        {

+            &utlLog(0, "Fail to link due to \"Fatal error: L6000U: Out of memory.\"\n");

+            next if ($state != $curState);

+            &generateLibTimestamp(\%lib) if (($opt{simulate} == 0) && ($state == STATE_PARTIAL_LINK));

+            if ($opt{stripDebugInfo})

+            {

+                &utlLog(0, "PLEASE EDIT \"pl_debug.txt\" TO REMOVE MORE DEBUG INFO FROM LIBRARIES\n");

+            }

+            else

+            {

+                &utlLog(0, "PLEASE ENABLE \"--strip-debug-info\" TO REMOVE DEBUG INFO OF LIBRARIES\n");

+            }

+        }

+        else

+        {

+            &utlLog(0, "Fail to link\n");    

+        }

+        last if ($state == $curState);

+    }

+    &setIni(\%ini, "state", $state) if ($opt{update});

+    &setIni(\%ini, "link", $link);

+    &writeIniFile(\%ini) unless ($opt{simulate});

+    system("date/T >>" . $FileLog);

+    system("time/T >>" . $FileLog);

+    $plink_end = time;

+    $Current_time = time;

+    system("echo T_E,Plink,P,$Current_time >> $optPathMust{dirSave}/PlinkTime.log");

+    if ($ENV{MBIS_BUILD_TIME_TMP} =~ /mbis/)

+    {

+    	system("echo T_S,Plink,P,$plink_start >> $ENV{MBIS_BUILD_TIME_TMP}");

+    	system("echo T_E,Plink,P,$plink_end >> $ENV{MBIS_BUILD_TIME_TMP}");

+    }

+    return $ret;

+}

+

+# -----------------------------------------------------------------------------

+

+exit &main($_argc, \@_argv);