Skip to content

Instantly share code, notes, and snippets.

@pbosetti
Last active May 2, 2023 11:50
Show Gist options
  • Save pbosetti/cdb6b6abee5263cd8939cc4377d78d4c to your computer and use it in GitHub Desktop.
Save pbosetti/cdb6b6abee5263cd8939cc4377d78d4c to your computer and use it in GitHub Desktop.
Script for generating stub dynamic libraries for cross compilation
#!/usr/bin/env ruby
# This script can create stub C libraries that are useful in cross-compilation
# tasks.
# Usage:
# ./mkstub.rb <path to original library file> [linux|macos]
# This will generate a stub file, a compiled shared object for the local system,
# and a shell script named compile_<libname>_stub.sh. The latter shall be copied
# to the target system (specified by the optional second argument), which
# doesn't have the library of matter, and executed, resulting in a shared object
# that represents a stub of the original library. A stub library has all the
# exported symbols of the original one, although they are all aliases to the
# same blank function. It can be safely used as a replacement of the original
# library for linking purpose.
class String
def red() "\e[0;31m" + self + "\e[0m"; end
def green() "\e[0;32m" + self + "\e[0m"; end
def yellow() "\e[0;33m" + self + "\e[0m"; end
def blue() "\e[0;34m" + self + "\e[0m"; end
end
lib_path = ARGV[0]
lib_name = File::basename(lib_path, ".*")
stub_name = "so_stub_for_#{lib_name}".sub("-", "_")
stub_file = "stubs.c"
puts "Generating stub library for symbols defined in #{lib_path}...".green
names = `nm #{lib_path}`
defsyms = names.split("\n")
.map {|n| l = n.split(" "); l[1..2]}
.filter {|e| e[0] == "T"}
# Local system
if (/darwin/ =~ RUBY_PLATFORM)
local_defsyms = defsyms.map {|n| "-Wl,-alias,_#{stub_name},#{n[1]}"}
local_lib_ext = ".dylib"
else
local_defsyms = defsyms.map {|n| "-Wl,--defsym,#{n[1]}=#{stub_name}"}
local_lib_ext = ".so"
end
# Target system
target = ARGV[1] || "linux"
if (/^[lL]/ =~ target)
target = "Linux"
lib_ext = ".so"
defsyms.map! {|n|
n[1] = n[1][1..-1] if n[1][0] == "_"
"-Wl,--defsym,#{n[1]}=#{stub_name}"
}
elsif (/^[mM]/ =~ target)
target = "MacOS"
lib_ext = ".dylib"
defsyms.map! {|n| "-Wl,-alias,_#{stub_name},#{n[1]}"}
else
puts "Unsupported target system #{target}".red
exit -1
end
puts "Found #{defsyms.length} symbols"
c_file = DATA.read
File.write(stub_file, c_file)
puts "Created stub file #{stub_file}"
puts "Compiling with command:"
defsym = defsyms.join(" ")
local_defsym = local_defsyms.join(" ")
defines = "-DFNAME=#{stub_name}";
puts "g++ -shared -Wall -fPIC #{defines} stubs.c #{local_defsyms[1]} ".blue + "[#{local_defsyms.length} more]".red + " -o #{lib_name}#{lib_ext}".blue
local_compile_cmd = "-shared -Wall -fPIC #{defines} #{local_defsym} -o #{lib_name}#{local_lib_ext} -Wno-deprecated"
compile_cmd = "-shared -Wall -fPIC #{defines} #{defsym} -o #{lib_name}#{lib_ext} -Wno-deprecated"
system("gcc " + local_compile_cmd + " stubs.c")
puts "Stub library generated as " + "#{lib_name}#{local_lib_ext}".green
puts "Generating script for target system #{target.green}..."
File.open("compile_#{lib_name}_stub.sh", "w") do |f|
f.print <<~EOF
#!/bin/sh
# Stub generator for creating stub #{lib_name} on #{target} systems
# Check OS
if [[ $OSTYPE != #{target == "Linux" ? "linux" : "darwin"}* ]]; then
echo "This script is supposed to run on #{target} only."
exit 1
fi
# default compiler
if [ -z "$CC"]; then CC=gcc; fi
# find last line +1
SCRIPT_END=$(awk '/^__PAYLOAD__/ { print NR + 1; exit 0; }' $0)
STUB_FILE="#{lib_name}_stub.c"
# Extract file
echo "Creating $STUB_FILE (#{target} platform)"
tail -n +$SCRIPT_END $0 > $STUB_FILE
# compile
echo "Compiling $STUB_FILE > #{lib_name}#{lib_ext}"
${CC} #{compile_cmd} #{lib_name}_stub.c
exit 0
__PAYLOAD__
#{c_file}
EOF
end
File.chmod(0755, "compile_#{lib_name}_stub.sh")
puts "Script " + "compile_#{lib_name}_stub.sh".green + " generated. Copy that on target system and run it"
puts "Done.".green
__END__
// Created by mkstubs.rb script
#include <stdio.h>
#include <stdlib.h>
void FNAME() {
fprintf(stderr, "%s: this is a stub, real library not available\n", __func__);
abort();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment