#!/usr/bin/env ruby $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib') require 'mithril' module Elf def self.newfile ElfFile.new.tap {|x| x.filetype = ElfFlags::Type::ET_EXEC x.machine = ElfFlags::Machine::EM_X86_64 x.version = ElfFlags::Version::EV_CURRENT x.flags = 0 x.bits = 64 x.endian = :little x.interp = "/lib64/ld-linux-x86-64.so.2" } end def self.linkfiles(outfile, infiles) progbits = {} symbols = {} sect_map = {} sect_offsets = {} symbols = {} infiles.each {|infile| #link actual data (infile.progbits + infile.nobits).each {|inbit| outbit = (progbits[inbit.name] ||= Elf::ProgBits.new(inbit.name)) outbit.flags |= inbit.flags outbit.align = [inbit.align, outbit.align].max outbit.sect_type = inbit.sect_type sect_offsets[inbit] = outbit.data.tell sect_map[inbit] = outbit outbit.data.write inbit.data.read #TODO: Handle align } # Link together symbol table. Note we do not support versioning in the poc infile.symbols.each {|symbol| next if symbol.name.empty? next unless [Elf::STT::STT_OBJECT, Elf::STT::STT_FUNC, Elf::STT::STT_COMMON, Elf::STT::STT_NOTYPE].include? symbol.type if (symbols.include? symbol.name) next if symbol.weak? or symbol.undefined? raise ArgumentError.new "Duplicate definition of symbol #{symbol.name}" unless symbols[symbol.name].weak? or symbols[symbol.name].undefined? end symbols[symbol.name] = symbol.clone.tap{|outsym| unless outsym.undefined? outsym.sectoffset += sect_offsets[outsym.section] outsym.section = sect_map[outsym.section] end } } } #TODO: mark per-object section symbols progbits.values.each {|progbit| outfile.progbits << progbit } symbols.values.each {|sym| outfile.symbols << sym} infiles.map(&:relocations).flatten.each {|rela| outfile.relocations << rela.clone.tap{|x| x.offset += sect_offsets[x.section] x.section = sect_map[x.section] name = x.symbol.name x.symbol = symbols[name] x.is_dynamic = true raise ArgumentError.new "Undefined symbol #{name}" if(x.symbol.nil?) } } #TODO: Produce dynsym raise "Undefined entry point _start " unless symbols.include? "_start" entry = symbols["_start"] entry.section.addr = 0x40000 outfile.entry = entry.sectoffset + entry.section.addr outfile end end file = Elf::linkfiles(Elf::newfile, ARGV[1..-1].map{|x| Elf::Parser::from_file(x)}) Elf::Writer.to_file(ARGV[0], file) `chmod +x '#{ARGV[0]}'`