--- /dev/null
+#!/usr/bin/perl -w
+
+#
+#
+# Wrap a kernel module (.ko) so that all undefined functions
+# are resolved at compile time to wrapper that have the template
+# as given here
+#
+# TODO: parameterize hypercall numbers
+# TODO: remove printk special case
+#
+
+use Getopt::Long;
+use File::Basename;
+
+$user=0;
+$kern=0;
+
+$V3_BORDER_OUT_CALL_NR = 0x6003;
+$V3_BORDER_IN_RET_NR   = 0x6004;
+
+&GetOptions("user"=>\$user, "kern"=>\$kern);
+
+$#ARGV==0 or die "christo.pl [--user|--kern] <object-file>\n";
+
+$of=shift; chomp($of);
+($ofstem) = split(/\./,fileparse($of)); 
+
+if ($user) { 
+  
+  print "Wrapping $of to produce $ofstem-wrapped\n";
+  
+  # First, find all undefined symbols that are text
+  @funcs = ExtractUndefinedFunctions($of);
+  
+  print "Wrapping the following functions\n";
+  print join("\n", @funcs),"\n";
+  
+  print "Generating wrapper $ofstem\_wrapper.c\n";
+  
+  open(WRAPPER, ">$ofstem\_wrapper.c");
+  GenerateUserWrapper(WRAPPER, @funcs);
+  close(WRAPPER);
+
+  print "Compiling wrapper\n";
+  
+  CompileUserWrapper($ofstem);
+
+
+  print "Linking with your supplied file $of to form $ofstem\_wrapped\n";
+
+  LinkUserWrapped($of,"$ofstem\_wrapper.o","$ofstem\_wrapped",@funcs);
+
+  print "Done.\n"
+
+} else {
+
+  print "Wrapping $of to produce $ofstem-wrapped\n";
+
+  @funcs = KernUndefFuncs($of);
+
+  
+  print "Wrapping the following functions\n";
+  print join("\n", @funcs), "\n";
+
+  print "Generating wrapper $ofstem\_wrapper.S\n";
+
+  open(WRAPPER,">$ofstem\_wrapper.S");
+  GenerateKernWrapper($ofstem, WRAPPER, @funcs);
+  close(WRAPPER);
+
+}
+
+sub CompileUserWrapper {
+  my $stem=shift;
+  
+  return system("gcc -c -fPIC $stem.c -o $stem.o");
+}
+
+
+sub LinkUserWrapped {
+  my ($of,$wrapper,$wrapped,@funcs) = @_;
+  
+  $cmd = "gcc ".join(" ", 
+                    join(" ",
+                         map {" -Xlinker --wrap=$_ "} @funcs
+                        ), 
+                    $of, $wrapper)." -o $wrapped";
+  
+  print $cmd;
+  return system $cmd;
+
+}
+
+sub KernUndefFuncs {
+    my $of = shift;
+    my $line;
+    my @funcs;
+    my @lines;
+    my @final_funcs;
+    my %sysmap;
+
+    print STDERR "Acquiring all undefined symbols from file\n";
+
+    open(OBJDUMP, "objdump -t $of |") or die "Cannot open file $of\n";
+
+    while ($line=<OBJDUMP>) {
+        if ($line =~ /.*\*UND\*\s+\S+\s+(\S+)$/) {
+            push @funcs, $1;
+        }
+    }
+
+    close(OBJDUMP);
+
+    print STDERR "Finding all functions listed in System.map\n";
+
+    open(MAP,"System.map") or die "Cannot open System.map\n";
+
+    while ($line=<MAP>) { 
+       chomp($line);
+       my ($addr,$type,$name) = split(/\s+/,$line);
+       if (($type eq "t") or ($type eq "T")) { 
+           $sysmap{$name}=1;
+       }
+    }
+
+    close(MAP);
+
+    foreach $func (@funcs) { 
+       if (defined($sysmap{$func}) and ($func ne "printk") and ($func ne "mcount")
+        and ($func ne "__cyg_profile_func_enter") and ($func ne "__cyg_profile_func_exit")) {
+           print STDERR "ACCEPT  $func\n";
+           push @final_funcs, $func;
+       } else {
+           print STDERR "DISCARD $func\n";
+       }
+    }
+
+    return @final_funcs;
+}
+
+sub ExtractUndefinedFunctions {
+  my $of=shift;
+  my $line;
+  my @funcs;
+
+  open(OBJDUMP,"objdump -t $of |") or die "Cannot open file $of\n";
+
+  while ($line=<OBJDUMP>) { 
+    if ($line =~ /.*\*UND\*\s+\S+\s+(\S+)$/) { 
+      push @funcs, $1;
+    }
+  }
+
+  close(OBJDUMP);
+
+  return @funcs;
+
+}
+
+sub GenerateUserWrapper  {
+  my $file=shift;
+  
+
+  while ($func=shift) {
+    print $file <<ENDF;
+
+int __wrap_$func(void *a1, 
+                void *a2,
+                void *a3,
+                void *a4,
+                void *a5,
+                void *a6,
+                void *a7,
+                void *a8) 
+{
+ asm volatile("vmmcall"
+        :"=a" (ret)
+        :"0" ($V3_GUARD_EXIT_HCALL_NR));
+  return __real_$func(a1,a2,a3,a4,a5,a6,a7,a8);
+ asm volatile("vmmcall"
+        :"=a" (ret)
+        :"0" ($V3_GUARD_ENTER_HCALL_NR));
+}
+
+ENDF
+
+  }
+}
+
+sub GenerateKernWrapper {
+    my $fname=shift;
+    my $file=shift;
+    my @funcs=@_;
+
+  open(MKFILE,">Makefile");
+  open(CFILE, ">wrap_impl.c");
+
+    print MKFILE "### THIS FILE IS AUTOMATICALLY GENERATED ###\n";
+    print MKFILE "EXTRA_LDFLAGS = ";
+
+    print $file "/* THIS FILE IS AUTOMATICALLY GENERATED */\n";
+    print $file ".text\n.align 4\n";
+    print $file map { ".globl __wrap_$_\n" } @funcs;
+    print $file map { ".globl __wrap_impl_$_\n" } @funcs;
+ 
+    print CFILE "/* THIS FILE IS AUTOMATICALLY GENERATED */\n";
+    print CFILE "#include <linux/kernel.h>\n\n";
+    #print CFILE "extern int wrapper_count;\n\n";
+    #print CFILE "static int border_count = 0;\n\n";
+    print CFILE map { "void __wrap_impl_$_(void);\n\n" } @funcs;
+
+    foreach $func (@funcs) {
+#__wrap_$func:
+        #nop
+        #nop
+        #nop
+        #nop
+        #nop
+        #nop
+        #nop
+        #pushq %rbp
+        #movq  %rsp, %rbp
+        #pushq %rax
+        #pushq %rbx
+        #pushq %rcx
+        #pushq %rdx
+        #pushq %rdi
+        #pushq %rsi
+        #pushq %r8
+        #pushq %r9
+        #pushq %r10
+        #pushq %r11
+        #pushq %r12
+        #pushq %r13
+        #pushq %r14
+        #pushq %r15
+        #callq __wrap_impl_$func
+        #popq %r15
+        #popq %r14
+        #popq %r13
+        #popq %r12
+        #popq %r11 
+        #popq %r10
+        #popq %r9
+        #popq %r8 
+        #popq %rsi
+        #popq %rdi
+        #popq %rdx
+        #popq %rcx
+        #popq %rbx
+        #popq %rax
+        #popq %rbp
+        #jmp __real_$func
+        #nop
+        #nop
+        #nop
+        #nop
+        #nop
+
+    print $file <<ENDF;
+
+__wrap_$func:
+        popq  %r11
+        pushq %rax
+        movq  \$$V3_BORDER_OUT_CALL_NR, %rax
+        vmmcall
+        popq  %rax
+        callq __real_$func
+        pushq %rax
+        movq \$$V3_BORDER_IN_RET_NR, %rax
+        vmmcall
+        popq  %rax
+        pushq %r11
+        ret
+ENDF
+
+    print MKFILE "--wrap $func ";
+
+    print CFILE <<ENDF;
+void __wrap_impl_$func(void) {
+
+        //wrapper_count++;
+        //printk("exit: $func\\n");
+        //printk("wc: %d (%s)\\n", wrapper_count,__func__);
+}
+
+ENDF
+
+  }
+  print MKFILE "\n\nldflags-y := \$(EXTRA_LDFLAGS)\n";
+
+# TODO: change this to not be hardcoded!
+  my $kerndir = "/v-test/guard_modules/gm_guest/kyle_gl/";
+  print MKFILE <<ENDF;
+KERNDIR=$kerndir
+  
+obj-m := $fname\_wrapped.o
+    
+$fname\_wrapped-objs := $fname.ko $fname\_wrapper.o wrap_impl.o
+
+$fname\_wrapped.ko: $fname.ko $fname\_wrapper.o wrap_impl.o
+\tmake -C \$(KERNDIR) M=\$(PWD) modules
+
+clean:
+\tmake -C \$(KERNDIR) M=\$(PWD) clean
+
+ENDF
+
+  close(MKFILE);
+  close(CFILE);
+
+}
 
--- /dev/null
+#!/usr/bin/ruby
+
+# Kyle C. Hale (c) 2012/2013
+#
+# Ruby implementation of a tool to identify the callbacks
+# in a driver given the source for the driver and it's compiled kernel
+# object. It does this by finding all static functions in the module and scanning
+# the source to identify sites where a static function is being assigned to a variable.
+#
+# Dependencies: 
+#               json ruby gem
+#
+# By default, all callbacks will be added as valid entry points into the module.
+# For now, entries must manually be added and deleted by module author
+#
+# TODO:     
+#           - remove hardcoded directories from this and chirsto script
+#
+#
+# NOTES:
+#
+# - .i files are generated to confirm callback functions
+#
+# - source files and makefiles must be in same dir. Wont work for big
+#   multi-level hierarchical source trees
+#
+# - this produces JSON output, including valid entry points, which 
+#   (both entering into callback functions and returning from calls
+#   to the kernel) can be modified by driver developer
+#
+# - this modifies the original driver source, as well as the original
+#   driver Makefile
+#
+# - You must provide your **GUEST'S** System.map file for this script to reference
+
+require 'optparse'
+require 'rubygems'
+require 'json'
+
+CHRISTO = "/root/kyle/kyle/bnx/christo-s.pl"
+GM_INIT = "v3_guard_mod_init"
+GM_INIT_INTERNAL = "__guard_mod_init_internal"
+GM_MACRO = "GM_ENTRY_REQUEST"
+WRAP_FILE = "entry_wrapper.S"
+
+GUARD_INIT_NR  = 0x6000
+GUARD_ENTER_NR = 0x6001
+GUARD_EXIT_NR  = 0x6002
+
+
+def replace_callback_assigns(callbacks, file)
+
+end
+
+# ================== OPTIONS ========================= 
+
+options = {}
+
+optparse = OptionParser.new do|opts|
+
+  opts.banner = "Usage: guard_modules.rb [options] ..."
+
+  options[:verbose] = false
+  opts.on( '-v', '--verbose', 'Allow verbose output') do
+      options[:verbose] = true
+  end
+  
+  opts.on( '-h', '--help', 'Show this information') do
+      puts opts
+      exit
+  end
+
+  opts.on('-s', '--source <driver,src,files,...,>', '(C) source code for driver module') do |file|
+      options[:srcfile] = file.split(',')
+  end
+  
+  opts.on('-p', '--privs <requested,privileges>', 'Privileges requested. Currently numerical') do |privs|
+      options[:privs] = privs.split(',')
+  end
+
+  opts.on('-k', '--ko DRIVER KO', 'The compiled and linked kernel object for the driver') do |file|
+      options[:ko] = file
+  end
+  
+  opts.on('-m', '--makefile MAKEFILE', 'The driver\'s makefile') do |file|
+        options[:mf] = file
+  end
+
+  opts.on('-o', '--output OUTFILE', 'The file to which static module info will be written') do |file|
+    options[:out] = file
+  end
+
+  opts.on('-n', '--name NAME', 'The name of the guarded module') do |name|
+    options[:name] = name
+  end
+
+  options[:instr] = false
+  opts.on('i', '--instrument', 'Dump out the functions which are *NOT* to be instrumented, i.e. those that aren\'t callbacks')  do
+    options[:instr] = true
+  end
+end
+
+
+begin
+  optparse.parse!
+  mand = [:ko, :srcfile, :mf, :name]
+  missing = mand.select{ |parm| options[parm].nil? }
+  if not missing.empty?
+    puts "Missing options: #{missing.join(', ')}"
+    puts optparse
+    exit
+  end
+rescue OptionParser::InvalidOption, OptionParser::MissingArgument
+  puts $!.to_s
+  puts optparse
+  exit
+end
+
+# ===================== END OPTIONS ==============================
+
+$mkdir   = File.expand_path(File.dirname(options[:mf]))
+$mkbase  = File.basename(options[:mf])
+#TODO: this needs to be specified explicitly
+$srcpath = options[:srcfile][0]
+$srcdir  = File.expand_path(File.dirname(options[:srcfile][0]))
+$srcbase = File.basename(options[:srcfile][0])
+
+# global list of functions
+# TODO: THIS IS NOT WORKING!!!! e.g., on bnx2_find_max_ring, objdump is not finding static funs
+funcs = `objdump -t #{options[:ko]} | awk '\$3==\"F\" {print \$6}'`
+
+# only static functions
+stats = `objdump -t #{options[:ko]} | awk '\$2==\"l\"&&\$3==\"F\" {print \$6}'`
+
+callbacks = []
+
+puts "Running preprocessor..."
+prep_files = {}
+
+Dir.chdir($mkdir) do
+
+    # generate .i files (preprocessing)
+    ENV['PWD'] = Dir.pwd
+
+    abort("Problem cleaning") if not system "make clean > /dev/null"
+
+    #TODO parameterize this!
+    kerndir = "/v-test/guard_modules/gm_guest/kyle_gl/"
+
+    # run the kernel make process, and repeat the build of each .o with preprocessing directives
+    cmds = `make V=1 2>&1 | grep -P "gcc.*?\\s+\\-o\\s+.*?[^mod]\\.c" | sed 's/\\s\\+-c/ -E/g'| sed 's/\\(-o\\s\\+.*\\?\\)\\.o/\\1.i/g'`
+
+    Dir.chdir(kerndir) do
+        ENV['PWD'] = Dir.pwd
+
+        cmds.each do |i| 
+            abort ("Problem compiling.") if not system i
+        end
+    end
+
+    ENV['PWD'] = Dir.pwd
+
+    # read in preprocessed files
+    Dir.glob("#{$mkdir}/*.i", File::FNM_DOTMATCH).each do |fname|
+            prep_files[fname] = IO.readlines(fname)
+            puts "Reading in preprocessed file: #{fname}"
+            if not prep_files[fname]
+                puts "could not open #{fname}"
+                exit
+            end
+    end
+end
+
+ENV['PWD'] = Dir.pwd
+
+stats.each do |stat|
+  s = stat.chomp
+  defs = []
+  
+  # look for callback assignments, this tells us that this
+  # static function is indeed being used as a callback
+  prep_files.each_value do |src|
+    tmp_defs = src.grep(/.*=\s*#{s}\s*(;|,)/)
+    defs.concat(tmp_defs) if not tmp_defs.empty?
+  end
+
+  callbacks.push(s) if not defs.empty?
+
+  if (options[:verbose]) 
+    puts "Possible callback assignment for #{s} : " if not defs.empty?
+    defs.each {|i| print "\t#{i}\n"}
+  end
+  
+end
+
+if (options[:instr])
+    f = funcs.split("\n")
+    
+    puts "Searching for driver's module_init routine..."
+    mod_init = nil
+
+    # TODO: make this work for multiple files
+    File.open($srcpath, 'r') do |file|
+        new_str = file.read
+        if new_str =~ /module_init\((.*?)\)/ 
+            puts "Found it, it's #{$1}" 
+            mod_init = $1
+        end
+    end
+    
+    # back up (TODO: ALL) source/makefiles
+    `cp #{$mkdir}/#{$mkbase} #{$mkdir}/#{$mkbase}.bak`
+    `cp #{options[:srcfile][0]} #{$srcdir}/#{$srcbase}.bak`
+
+    # generate a restore script
+    shell = `which sh`
+    restore = <<-eos
+#!#{shell}
+for i in `ls *.bak | sed 's/\.bak//g'`; do mv $i.bak $i; done
+make clean && make
+rm -f *.i
+eos
+
+    # TODO: WARNING WARNING: assumes source files and makefiles are in same place
+    File.open("#{$srcdir}/gm_restore.sh", "w", 0744) do |file|
+        file.puts restore 
+    end
+
+    str = <<-eos
+/* BEGIN GENERATED */
+#define V3_GUARD_ENTER_HCALL_NR #{GUARD_ENTER_NR}
+#define V3_GUARD_EXIT_HCALL_NR  #{GUARD_EXIT_NR}
+#define V3_GUARD_INIT_HCALL_NR  #{GUARD_INIT_NR}
+#define V3_GUARDED_MODULE_ID   0xA3AEEA3AEEBADBADULL
+#define #{GM_MACRO}(fun) _gm_entry_req_##fun
+int __init #{GM_INIT} (void);
+eos
+
+# for assembly linkage
+callbacks.each do |c| 
+    str +=<<-eos "int _gm_entry_req_#{c}(void);\n"
+__asm__(".globl _gm_entry_req_#{c};\\
+   _gm_entry_req_#{c}:\\
+   popq  %r11;          \\
+   pushq %rax;\\
+   movq  $#{GUARD_ENTER_NR}, %rax;\\
+   vmmcall;\\
+   popq  %rax;\\
+   callq #{c};\\
+   pushq %rax;\\
+   movq  $#{GUARD_EXIT_NR}, %rax;\\
+   vmmcall;\\
+   popq  %rax;\\
+   pushq %r11;\\
+   ret;");
+eos
+end
+str += "/* END GENREATED */\n"
+
+end_str = <<-eos
+/* BEGIN GENERATED */
+int #{GM_INIT_INTERNAL} (void) __attribute__((section (".text"),no_instrument_function));
+
+int #{GM_INIT_INTERNAL} (void) {
+        int ret, ret_orig;
+
+        /* GUARD INIT */
+        __asm__ __volatile__ ("vmmcall"
+                      :"=a" (ret)
+                      :"0"  (V3_GUARD_INIT_HCALL_NR), "b" (V3_GUARDED_MODULE_ID));
+
+        if (ret < 0) {
+                printk("Guest GM: error initializing guarded module\\n");
+        } else {
+                printk("Guest GM: successfully initialized guarded module\\n");
+        }
+
+        ret_orig = #{mod_init}();
+
+        if (ret_orig < 0) { 
+                printk("Guest GM: error calling original init\\n");
+        } else {
+                printk("Guest GM: successfully called original driver init\\n");
+        }
+
+        /* GUARD EXIT */
+        __asm__ __volatile__("vmmcall" : "=a" (ret) : "0" (V3_GUARD_EXIT_HCALL_NR));
+
+        if (ret < 0) {
+                printk("Guest GM: error doing initial exit\\n");
+                return ret;
+        }
+
+        return ret_orig;
+}
+
+int __init #{GM_INIT} (void) {
+        return #{GM_INIT_INTERNAL}();
+}
+
+/* END GENERATED */
+eos
+
+# GENERATE THE CALLBACK WRAPPERS
+#
+# statements like 
+# .callback = myfunc;
+#
+# will be replaced with 
+# .callback = GM_ENTRY_REQUEST(myfunc);
+#
+# which expands to 
+# .callback = __gm_entry_req_myfunc;  (this is an assembly stub)
+#
+
+    callbacks.each do |c|
+      end_str += <<-eos
+      void  (*#{c}_ptr)() = #{c};
+eos
+    end
+
+    end_str += "/* END GENERATED */"
+
+    # append instrumentation functions to end of source file
+    new_str = ""
+    File.open($srcpath, "r") do |file|
+        new_str = file.read
+    end
+
+    # fixup module init calls with our own
+    new_str = new_str.gsub(/__init #{mod_init}/, "#{mod_init}")
+    new_str = new_str.gsub(/module_init\(.*?\);/, "module_init(#{GM_INIT});")
+
+    # fixup callback assignments with our macro
+    # TODO: make this work for multiple source files
+    callbacks.each do |c|
+        new_str = new_str.gsub(/(.*=\s*)#{c}(\s*(;|,))/) {
+            "#{$1}#{GM_MACRO}(#{c})#{$2}"
+        }
+    end
+
+    # put these at the top of the file, after the includes
+    new_str = new_str.insert(new_str.rindex(/#include.*?[>"]/), str)
+    new_str += end_str
+    
+    File.open($srcpath, "w+") do |file|
+        file.puts new_str
+    end
+
+    Dir.chdir($mkdir) do
+        abort ("Error cleaning") if not system "make clean > /dev/null 2>&1"
+        abort("Error rebuilding module") if not system "make > /dev/null 2>&1"
+    end
+
+end
+
+# if an outfile is specified, output static module info
+# in JSON format
+if (options[:out])
+  # First generate wrapped module
+
+  ko = File.expand_path(options[:ko])
+  puts "Running christo with Kernel object: " + ko
+  christo_output = `#{CHRISTO} --kern #{ko} 2>&1 | grep ACCEPT`
+  out_arr = []
+  christo_output.each_line { |x| out_arr.push(x.chomp.gsub(/\s*ACCEPT\s*/, '')) }
+
+  wrap_name = File.basename(ko).sub(/\.ko$/,'_wrapped.ko')
+
+  ret_points = []
+  out_arr.each do |x|
+      ret_points.push Array[x, `objdump -d #{wrap_name} | sed -n '/__wrap_#{x}>/,/ret/p' | grep vmmcall | tail -1 | cut -f1 -d':' | sed 's/\s//g'`.to_i(16)]
+  end
+
+  abort("Problem building wrapped module") if not system "make > /dev/null 2>&1"
+  static_info = {}
+  static_info['module_name'] = options[:name]
+
+  # get the content hash of .text
+  static_info['content_hash'] = `objdump -h #{wrap_name} | grep -P "\s+\.text\s+" | awk '{print "dd if=#{wrap_name} bs=1 count=$[0x" $3 "] skip=$[0x" $6 "] 2>/dev/null"}' | bash | md5sum - | awk '{print $1}'`.chomp
+  static_info['size'] = `objdump -h #{wrap_name} | grep -P "\s+\.text\s+" | awk '{print $3}'`.to_i(16)
+
+  # find offset to hcall (we've ensured it's in .text)
+  static_info['hcall_offset'] = `objdump -d #{wrap_name} | sed -n '/#{GM_INIT_INTERNAL}/,/ret/p'| grep vmmcall | cut -f1 -d':'|sed 's/\s//g'`.to_i(16)
+
+  # each callback function will have ONE valid entry point, the address of the VMMCALL
+  # in its function body
+  static_info['entry_points'] = callbacks.collect do |c|
+      c_off = `objdump -d #{wrap_name} | sed -n '/_gm_entry_req_#{c}>/,/vmmcall/p' | tail -1 | cut -f1 -d':' | sed 's/\s//g'`.to_i(16)
+      Array[c, c_off]
+  end
+
+  static_info['ret_points'] = ret_points
+  static_info['privileges'] = options[:privs]
+
+  File.open(options[:out], "w") do |file|
+      file.write(JSON.pretty_generate(static_info))
+  end
+
+else
+  callbacks.each {|c| puts c }
+end