Voodoo::I386NasmGenerator

i386 NASM Code Generator

The i386 NASM code generator generates i386 assembly code for use with the Netwide Assembler.

Calling Convention

Function arguments are pushed on the stack in reverse order, so that the first argument is pushed last. Each argument occupies one word of stack space. These arguments are removed from the stack by the caller after the called function returns.

The return value is passed in eax.

Call Frames

Call frames have the following layout:

argn
:
arg1
arg0   <-- ebp + 8
oldeip <-- ebp + 4
oldebp <-- ebp
local0 <-- ebp - 4
local1 <-- ebp - 8
:
localn <-- esp

Callee-Save Registers

ebp, ebx, edi, esi, and esp are callee-save registers.

All other registers are caller-save registers.

Constants

WORDSIZE

Public Class Methods

new(params = {}) click to toggle source
# File voodoo/generators/i386_nasm_generator.rb, line 43
def initialize params = {}
  # Number of bytes in a word
  @WORDSIZE_BITS = 2
  @WORDSIZE = 1 << @WORDSIZE_BITS
  # Word name in NASM lingo
  @WORD_NAME = 'dword'
  # Default alignment for code
  @CODE_ALIGNMENT = 0
  # Default alignment for data
  @DATA_ALIGNMENT = @WORDSIZE
  # Default alignment for functions
  @FUNCTION_ALIGNMENT = 16
  # Stack alingment
  @STACK_ALIGNMENT_BITS = @WORDSIZE_BITS
  @STACK_ALIGNMENT = 1 << @STACK_ALIGNMENT_BITS
  # Register used for return values
  @RETURN_REG = :eax
  # Accumulator index
  @AX = :eax
  # Base index
  @BX = :ebx
  # Count index
  @CX = :ecx
  # Data index
  @DX = :edx
  # Base pointer
  @BP = :ebp
  # Stack pointer
  @SP = :esp
  # Registers used to store locals
  @LOCAL_REGISTERS = []
  @NLOCAL_REGISTERS = @LOCAL_REGISTERS.length
  @LOCAL_REGISTERS_SET = Set.new @LOCAL_REGISTERS
  @SAVE_FRAME_REGISTERS = [:ebx, :edi, :esi, :esp, :ebp]
  @SAVED_FRAME_LAYOUT = {}
  @SAVE_FRAME_REGISTERS.each_with_index { |r,i| @SAVED_FRAME_LAYOUT[r] = i }
  @TEMPORARIES = [:ebx]
  super params
  @saved_registers = []
  in_section(:data) { emit "extern _GLOBAL_OFFSET_TABLE_\n" }
  @features.merge!          :'bits-per-word' => '32',
    :'byte-order' => 'little-endian',
    :'bytes-per-word' => '4'
end

Public Instance Methods

arg_offset(n) click to toggle source

Returns the offset of the nth argument.

# File voodoo/generators/i386_nasm_generator.rb, line 90
def arg_offset n
  8 + (n * @WORDSIZE)
end
begin_function(formals, nlocals) click to toggle source

Emits function preamble and declare formals as function arguments.

# File voodoo/generators/i386_nasm_generator.rb, line 95
def begin_function formals, nlocals
  environment = Environment.new @environment
  @environment = environment
  emit "push #{@BP}\nmov #{@BP}, #{@SP}\n"
  formals.each_with_index do |arg,i|
    environment.add_arg arg, arg_offset(i)
  end
  emit "sub #{@SP}, #{nlocals * @WORDSIZE}\n"      
end
call(func, *args) click to toggle source

Calls a function.

# File voodoo/generators/i386_nasm_generator.rb, line 106
def call func, *args
  revargs = args.reverse
  revargs.each { |arg| push arg }
  use_value "call", func
  if args.length > 0
    emit "add esp, #{@WORDSIZE * args.length}\n"
  end
end
load_got_address(reg) click to toggle source

Loads the address of the global offset table into the given register.

# File voodoo/generators/i386_nasm_generator.rb, line 116
def load_got_address reg
  lbl = gensym
  emit "call #{lbl}\n"
  emit "#{lbl}:\n"
  emit "pop #{reg}\n"
  emit "add #{reg}, _GLOBAL_OFFSET_TABLE_ + $$ - #{lbl} wrt ..gotpc\n"
  reg
end
load_symbol_from_got(symbol, reg) click to toggle source

Loads a symbol from the global offset table.

# File voodoo/generators/i386_nasm_generator.rb, line 126
def load_symbol_from_got symbol, reg
  load_got_address reg
  emit "mov #{reg}, [#{reg} + #{symbol} wrt ..got]\n"
  reg
end
local_offset_or_register(n) click to toggle source

If the nth local is stored in a register, returns that register. Otherwise, returns the offset from the frame pointer.

# File voodoo/generators/i386_nasm_generator.rb, line 134
def local_offset_or_register n
  (n + 1) * -@WORDSIZE
end
push(value) click to toggle source

Push a word on the stack

# File voodoo/generators/i386_nasm_generator.rb, line 139
def push value
  value_ref = load_value value, "ebx"
  emit "push dword #{value_ref}\n"
end
tail_call(fun, *args) click to toggle source

Call a function, re-using the current call frame if possible

# File voodoo/generators/i386_nasm_generator.rb, line 145
def tail_call fun, *args
  if args.length > @environment.args
    # Not enough space to do proper tail call; do normal call instead
    emit "; not enough space for proper tail call; changed to regular call\n"
    ret :call, fun, *args
  else
    # If any arguments are going to be overwritten before they are
    # used, save them to new local variables and use those instead.
    (args.length - 1).downto(0) do |i|
      arg = args[i]
      next unless symbol?(arg)
      old_arg_offset = @environment[arg]
      next if old_arg_offset == nil || old_arg_offset < 0
      # arg is an argument that was passed on the stack.
      new_arg_offset = arg_offset i
      next unless old_arg_offset > new_arg_offset
      # arg will be overwritten before it is used.
      # Save it in a newly created temporary variable,
      # then use that instead.
      newsym = @environment.gensym
      let newsym, arg
      args[i] = newsym
    end

    # Same for the function we will be calling.
    if symbol?(fun)
      offset = @environment[fun]
      if offset != nil && offset > 0
        newsym = @environment.gensym
        let newsym, fun
        func = newsym
      end
    end

    # Set arguments
    if args.length > 0
      (args.length - 1).downto(0).each do |i|
        arg = args[i]
        
        value_ref = load_value arg, :eax
        newarg_ref = "[ebp + #{arg_offset i}]"
        # Elide code if source is same as destination
        unless value_ref == newarg_ref
          if memory_operand?(value_ref)
            emit "mov eax, #{value_ref}\n"
            value_ref = :eax
          end
          emit "mov #{@WORD_NAME} #{newarg_ref}, #{value_ref}\n"
        end
      end
    end

    # Tail call
    emit "mov esp, ebp\npop ebp\n"
    use_value "jmp", fun
  end
end
use_value(operation, value) click to toggle source
# File voodoo/generators/i386_nasm_generator.rb, line 203
def use_value operation, value
  value_ref = load_value value, :eax
  emit "#{operation} #{value_ref}\n"
end
word(value) click to toggle source

Define a machine word with the given value

# File voodoo/generators/i386_nasm_generator.rb, line 209
def word value
  emit "dd #{value}\n"
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.