#!/usr/bin/perl
#
# Copyright (c) 1994-1996, 1998-2000 University of Utah and the Flux Group.
# All rights reserved.
# 
# This file is part of the Flux OSKit.  The OSKit is free software, also known
# as "open source;" you can redistribute it and/or modify it under the terms
# of the GNU General Public License (GPL), version 2, as published by the Free
# Software Foundation (FSF).  To explore alternate licensing terms, contact
# the University of Utah at csl-dist@cs.utah.edu or +1-801-585-3271.
# 
# The OSKit is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GPL for more details.  You should have
# received a copy of the GPL along with the OSKit; see the file COPYING.  If
# not, write to the FSF, 59 Temple Place #330, Boston, MA 02111-1307, USA.
#

# This little script uses GNU ld to build a 32-bit MultiBoot boot image
# from a MultiBoot kernel and a set of boot modules.

#
# Usage: mkmb2 [options] [files ...]
#
# Options:
#   -x filename
#      Use <filename> as the MultiBoot adaptor file instead of the default.
#   -o filename
#      Specify the output <filename> of the resulting multiboot image. The
#      default is "Image" in the current directory.
#   -c 'string'
#      Specify a command line <string> to pass to the kernel when it is
#      invoked.
#   -save-temps
#      Debugging option to save temporary files instead of deleting them
#      after the image is built.
#   -stdin
#      Take file specifications from stdin, in addition to those on the
#      command line. This is useful when using another script to build
#      a list of file specifications which can then be piped into this
#      script.
#   [files ...]
#      A list of file specifications in the format pathname1:pathname2,
#      where pathname1 is the name of a local module which is placed in the
#      boot image. If :pathname2 is provided, it specifies the name to give
#      the module in the image. If :pathname2 is omitted, it defaults to
#      pathname1.
#
#   The first file specification is typically the name of the Oskit kernel.
#

$bootdir = $ENV{"BOOTDIR"};
$cc = $ENV{"CC"};
$ld = $ENV{"LD"};
if (!$cc) { $cc = "gcc"; }
if (!$ld) { $ld = "ld"; }
if (!$bootdir) { $bootdir = "/usr/lib/boot"; }
$bb="$bootdir/mbboot.o";

@modules = ();
$outfile="Image";
$savetemps=0;
$fromstdin = 0;
$ldopts="-Ttext 100000 -n";
$cmdlinefile="bootadapter_cmdline";

if ($#ARGV == -1) {
        print "Usage: mkmb2 [option | filename]\n";
	exit;
}

# Parse the command-line options
while ($#ARGV >= 0) {
        if ($ARGV[0] eq "-x") { $bb = $ARGV[1]; shift @ARGV; }
	elsif ($ARGV[0] eq "-o") { $outfile = $ARGV[1]; shift @ARGV; }
	elsif ($ARGV[0] eq "-c") { $cmdline = $ARGV[1]; shift @ARGV; }
	elsif ($ARGV[0] eq "-stdin") { $fromstdin = 1; }
	elsif ($ARGV[0] eq "-save-temps") { $savetemps = 1; }
	elsif ($ARGV[0] eq "-K") { $NOBIN{$ARGV[1]} = 1; }
	else { push(@modules, $ARGV[0]); }
	shift @ARGV;
}

# Add in anything from stdin if they've asked for it.
if ($fromstdin) {
        while (<STDIN>) {
		@words = split;
		push(@modules, @words);
	}
}

# Link in the command line if they specified one
if ($cmdline) {
        open(CMD, ">$outfile.cmdline") || 
	  die "could not open cmdline file: $!\n";
	print CMD "$cmdline\n";
	push(@modules, "${outfile}.cmdline:${cmdlinefile}");
	close(CMD);
}

open(OUT, ">${outfile}.mods.S") || die "Could not open outfile: $!\n";

print OUT ".data; .globl boot_modules,_boot_modules; boot_modules:; _boot_modules:\n";

@files = ();
@linksmade = ();

# Wrap each of the input files in a .o file.
# At the same time, build an assembly language module
# containing a table describing the boot modules.

foreach $module (@modules) {

        $file = $module;
	$file =~ s/:.*$//;
	
	$string = $module;
	$string =~ s/^[^:]*://;
	
	if (!length($string)) { $string = $file; }
	
	if (!$FOUND{$file}) {	# Must be unique or we get linker probs
	        push(@files, $file); #This is where the shell script bogged down
	      }
	$FOUND{$file} = 1;

	# Convert all non alphanumeric characters to underscores.
	# The BFD binary input format will do the same thing
	# to produce the symbol names that it wraps around the input files
	$sym_name = $file;
	$sym_name =~ s/[^a-zA-Z0-9]/_/g;
	
	print OUT<<EOL;
.long _binary_${sym_name}_start
.long _binary_${sym_name}_end
.long 1f
.data 2
1: .ascii "${string}\\0"
.data
EOL
}

print OUT ".long 0; .data; .align 4\n";
close(OUT);

# Turn the assembly discription module into a .o
$res = system("${cc} -c -o ${outfile}.mods.o ${outfile}.mods.S");
if ($res) {
  &cleanup();
  die "FATAL: C compiler ($cc) failed\n";
}

# Parse the file list, doing any necessary magic to the files.
# I'm leaving this in for now for experimentation with different
# linker args to try to get library files to load properly.
$filelist = "";
foreach $file (@files) {
  ## Escape any '$' that occur in file names (happens a lot with Java classes)
  $file =~ s/\$/\\\$/g;
  if ($NOBIN{$file}) {
    $filelist .= "$file ";
  } else {
    $filelist .= "$file ";
  }
}

# Link the MultiBoot boot adaptor file and hte module vector file
# with the boot module files.
# Use the binary bfd backend for the input bmod files.
$res = system("$ld $ldopts -o $outfile $bb $outfile.mods.o -b binary $filelist -b elf32-i386");

if ($res) {
  &cleanup();
  die "FATAL: linker ($aoutld) failed: $res\n";
}

# Nuke our temp files, unless asked not to.
if (!$savetemps) {
  system("rm -f $outfile.mods.S $outfile.mods.o ${outfile}.cmdline");
}

exit(0);

# A stub if we need to do any cleanup on failure.
sub cleanup {
  ;
}
