#! @PERL@

# Generic information about a purpose of this script can be found in
# link_tool_exe_linux.in.
#
# Solaris specific notes:
#
# - load address has to be specified in the mapfile, there is no command line
#   option to achieve that
#
# - mapfile version 2 is used
#
# - information about Solaris linker can be found in its man page
#   (http://download.oracle.com/docs/cd/E19253-01/816-5165/ld-1/index.html)
#   and in Oracle's Linker and Libraries Guide
#   (http://download.oracle.com/docs/cd/E19963-01/html/819-0690/index.html)
#

use warnings;
use strict;
use File::Temp qw/tempfile unlink0/;
use Fcntl qw/F_SETFD/;

# expect at least: alt-load-address gcc -o foo bar.o
die "Not enough arguments"
    if (($#ARGV + 1) < 5);

my $ala = $ARGV[0];

# check for plausible-ish alt load address
die "Bogus alt-load address"
    if (length($ala) < 3 || index($ala, "0x") != 0);

# the cc invocation to do the final link
my $cc = $ARGV[1];

# and the 'restargs' are argv[2 ..]

# create a temporary mapfile
(my $fh, my $path) = tempfile();

# reset FD_CLOEXEC flag
fcntl($fh, F_SETFD, 0)
    or die "Can't clear close-on-exec flag on temp fh: $!";

# safely unlink the file
unlink0($fh, $path)
    or die "Error unlinking file $path safely";
undef $path;

# fill it with data
#
# this is a bit tricky, the problem is that the following condition has to be
# true for both PT_LOAD segments:
# (phdr->p_vaddr & PAGEOFFSET) == (phdr->p_offset & PAGEOFFSET)
# if it doesn't hold then the kernel maps a segment as an anon mapping instead
# of a file mapping (which, for example, breaks reading debug information)
print $fh <<"END";
\$mapfile_version 2
LOAD_SEGMENT text { VADDR = $ala; ROUND = 0x1000 };
LOAD_SEGMENT data { ROUND = 0x1000 };
END

# build up the complete command here:
# 'cc' -Wl,-Mtmpfile 'restargs'

my $cmd="$cc -Wl,-M/proc/$$/fd/" . fileno($fh);

# add the rest of the parameters
foreach my $n (2 .. $#ARGV) {
    $cmd = "$cmd $ARGV[$n]";
}

#print "link_tool_exe_solaris: $cmd\n";


# execute the command:
my $r = system("$cmd");

if ($r == 0) {
    exit 0;
} else {
    exit 1;
}
