Parent

Voodoo::NasmGenerator

NASM Code Generator

The NASM code generator is a common base class for generators that output assembly code for use with the Netwide Assembler.

This class is used by both the I386NasmGenerator and the AMD64NasmGenerator, and contains the functionality that is common to both.

To use the functionality from this class, a subclass must define the following methods and constants:

Public Class Methods

new(params = {}) click to toggle source
# File voodoo/generators/nasm_generator.rb, line 27
def initialize params = {}
  super params
  @if_labels = []
  @output_file_suffix = '.asm'
end

Public Instance Methods

action_to_mnemonic(action) click to toggle source

Translates a Voodoo action name to an x86 mnemonic

# File voodoo/generators/nasm_generator.rb, line 725
def action_to_mnemonic action
  case action
  when :asr
    :sar
  when :bsr
    :shr
  else
    action
  end
end
auto_bytes(n, register = @RETURN_REG) click to toggle source

Allocates n bytes on the stack and stores a pointer to the allocated memory in the specified register. The number of bytes is rounded up to the nearest multiple of @STACK_ALIGNMENT.

# File voodoo/generators/nasm_generator.rb, line 187
def auto_bytes n, register = @RETURN_REG
  if n.kind_of? Integer
    auto_bytes_immediate n, register
  else
    load_value_into_register n, register
    auto_bytes_register register, register
  end
end
auto_bytes_immediate(nbytes, register) click to toggle source

Implements auto_bytes where the number of bytes to allocate is given as an immediate value.

# File voodoo/generators/nasm_generator.rb, line 198
def auto_bytes_immediate nbytes, register
  nbytes = ((nbytes + @STACK_ALIGNMENT - 1) >> @STACK_ALIGNMENT_BITS) <<
    @STACK_ALIGNMENT_BITS
  emit "sub #{@SP}, #{nbytes}\n"
  emit "mov #{register}, #{@SP}\n" if register != @SP
end
auto_bytes_register(nbytes, register = @RETURN_REG) click to toggle source

Implements auto_bytes where the number of bytes is supplied in a register.

# File voodoo/generators/nasm_generator.rb, line 207
def auto_bytes_register nbytes, register = @RETURN_REG
  emit "add #{nbytes}, #{@STACK_ALIGNMENT - 1}\n"
  emit "shr #{nbytes}, #{@STACK_ALIGNMENT_BITS}\n"
  emit "shl #{nbytes}, #{@STACK_ALIGNMENT_BITS}\n"
  emit "sub #{@SP}, #{nbytes}\n"
  emit "mov #{register}, #{@SP}\n" if register != @SP
end
auto_words(n, register = @RETURN_REG) click to toggle source

Allocates n words on the stack and stores a pointer to the allocated memory in the specified register.

# File voodoo/generators/nasm_generator.rb, line 217
def auto_words n, register = @RETURN_REG
  if n.kind_of? Integer
    auto_bytes_immediate n * @WORDSIZE, register
  else
    load_value_into_register n, register
    if @STACK_ALIGNMENT_BITS > @WORDSIZE_BITS
      emit "add #{register}, " +
        "#{(1 << @STACK_ALIGNMENT_BITS >> @WORDSIZE_BITS) - 1}\n"
      emit "shr #{register}, #{@STACK_ALIGNMENT_BITS - @WORDSIZE_BITS}\n"
      emit "shl #{register}, #{STACK_ALIGNMENT_BITS}\n"
    else
      emit "shl #{register}, #{@WORDSIZE_BITS}\n"
    end
    emit "sub #{@SP}, #{register}\n"
    emit "mov #{register}, #{@SP}\n" if register != @SP
  end
end
begin_block(*code) click to toggle source

Begins a new block.

# File voodoo/generators/nasm_generator.rb, line 160
def begin_block *code
  # If entering a block at top level,
  # Save @BP, then set @BP to @SP
  if @environment == @top_level
    emit "push #{@BP}\n"
    emit "mov #{@BP}, #{@SP}\n"
  end
  environment = Environment.new @environment
  @environment = environment
end
byte(value) click to toggle source

Define a byte with the given value

# File voodoo/generators/nasm_generator.rb, line 78
def byte value
  emit "db #{value}\n"
end
comment(text) click to toggle source

Emit a comment

# File voodoo/generators/nasm_generator.rb, line 737
def comment text
  emit "; #{text}\n"
end
common_if(branch, x, y = nil) click to toggle source

Start a conditional using the specified branch instruction after the comparison.

# File voodoo/generators/nasm_generator.rb, line 631
def common_if branch, x, y = nil
  # Inverses of branches. E.g.
  #   cmp x, y
  #   jle somewhere
  # is the same as
  #   cmp y, x
  #   jgt somewhere
  inverse_branch = {
    :je => :je,
    :jge => :jl,
    :jg => :jle,
    :jl => :jge,
    :jle => :jg,
    :jne => :jne,
  }

  y_ref = load_value y, @DX
  x_ref = load_value x, @AX
  if immediate_operand?(x_ref)
    # Can't have an immediate value as the first operand.
    if immediate_operand?(y_ref)
      # Both are immediates. Put the first in a register.
      emit "mov #{@AX}, #{x_ref}\n"
      x_ref = @AX
    else
      # y isn't immediate; swap x and y.
      x_ref, y_ref = [y_ref, x_ref]
      branch = inverse_branch[branch]
    end
  elsif memory_operand?(x_ref) && memory_operand?(y_ref)
    # Can't have two memory operands. Move the first into a register.
    emit "mov #{@AX}, #{x_ref}\n"
    x_ref = @AX
  end
  truelabel = @environment.gensym
  falselabel = @environment.gensym
  @if_labels.push falselabel

  emit "cmp #{@WORD_NAME} #{x_ref}, #{y_ref}\n"
  emit "#{branch} #{truelabel}\n"
  emit "jmp #{falselabel}\n"
  emit "#{truelabel}:\n"
end
div(target, x, y) click to toggle source

Divide x by y and store the quotient in target

# File voodoo/generators/nasm_generator.rb, line 451
def div target, x, y
  eval_div x, y
  with_temporary do |temporary|
    target_ref = load_value target, temporary
    emit "mov #{target_ref}, #{@AX}\n"
  end
end
div2(target, x) click to toggle source

Divide target by x and store the quotient in target

# File voodoo/generators/nasm_generator.rb, line 460
def div2 target, x
  div target, target, x
end
dword(value) click to toggle source

Define a dword with the given value

# File voodoo/generators/nasm_generator.rb, line 83
def dword value
  emit "dd #{value}\n"
end
emit_align(alignment) click to toggle source

Alignment

# File voodoo/generators/nasm_generator.rb, line 123
def emit_align alignment
  emit "align #{alignment}\n"
end
emit_export(*symbols) click to toggle source

Export symbols from the current section

# File voodoo/generators/nasm_generator.rb, line 38
def emit_export *symbols
  case real_section_name(section)
  when ".text"
    symbols.each { |sym| emit "global #{sym}:function\n" }
  else
    symbols.each { |sym| emit "global #{sym}:data #{sym}.end-#{sym}\n" }
  end
end
emit_function_epilogue(formals = []) click to toggle source

Emits function epilogue.

# File voodoo/generators/nasm_generator.rb, line 132
def emit_function_epilogue formals = []
  emit "leave\n"
end
emit_import(*symbols) click to toggle source

Emits code to declare symbols as imported from an external object.

# File voodoo/generators/nasm_generator.rb, line 56
def emit_import *symbols
  emit "extern #{symbols.join ', '}\n"
end
emit_label(name) click to toggle source

Emits a label.

# File voodoo/generators/nasm_generator.rb, line 61
def emit_label name
  emit "#{name}:\n"
end
emit_label_size(name) click to toggle source
# File voodoo/generators/nasm_generator.rb, line 65
def emit_label_size name
  emit ".end:\n"
end
emit_label_type(name, type) click to toggle source
# File voodoo/generators/nasm_generator.rb, line 69
def emit_label_type name, type
  # nothing to do
end
emit_load_word(register, base, offset = 0) click to toggle source

Loads a word into a register.

# File voodoo/generators/nasm_generator.rb, line 275
def emit_load_word register, base, offset = 0
  if offset == 0
    emit "mov #{register}, [#{base}]\n"
  else
    emit "mov #{register}, [#{base} + #{offset} * #{@WORDSIZE}]\n"
  end
end
emit_store_word(register, base, offset = 0) click to toggle source

Stores the value of a register in memory.

# File voodoo/generators/nasm_generator.rb, line 397
def emit_store_word register, base, offset = 0
  if offset == 0
    emit "mov [#{base}], #{register}\n"
  else
    emit "mov [#{base} + #{offset} * #{@WORDSIZE}], #{register}\n"
  end
end
end_block() click to toggle source

Ends the current block.

# File voodoo/generators/nasm_generator.rb, line 172
def end_block
  # Restore old value of @environment
  @environment = @environment.parent

  # If returning to top level, restore old @BP
  emit "leave\n" if @environment == @top_level
end
end_function() click to toggle source

Ends a function body

# File voodoo/generators/nasm_generator.rb, line 137
def end_function
  if @environment == @top_level
    raise "Cannot end function when not in a function"
  else
    @environment = @top_level
  end
end
end_if() click to toggle source

End a conditional body

# File voodoo/generators/nasm_generator.rb, line 251
def end_if
  label = @if_labels.pop
  emit "#{label}:\n"
end
eval_div(x, y) click to toggle source

Perform division. The quotient is stored in @AX, the remainder in @DX.

# File voodoo/generators/nasm_generator.rb, line 498
def eval_div x, y
  with_temporary do |temporary|
    x_ref = load_value_into_register x, @AX
    y_ref = load_value y, temporary
    emit "mov #{@DX}, #{@AX}\n"
    emit "sar #{@DX}, #{@WORDSIZE * 8 - 1}\n"
    if immediate_operand?(y_ref)
      set_register temporary, y_ref
      emit "idiv #{temporary}\n"
    else
      emit "idiv #{@WORD_NAME} #{y_ref}\n"
    end
  end
end
eval_expr(words, register = @RETURN_REG) click to toggle source

Evaluate an expression. The result is stored in register (@RETURN_REG by default). The following registers may be clobbered: @AX, @CX, @DX

# File voodoo/generators/nasm_generator.rb, line 516
def eval_expr words, register = @RETURN_REG
  if words.length == 1
    if words[0] == 0
      emit "xor #{register}, #{register}\n"
    else
      load_value_into_register words[0], register
    end
  else
    op = words[0]
    case op
    when :asr, :bsr, :rol, :ror, :shl, :shr
      if integer? words[2]
        y_ref = words[2]
      else
        load_value_into_register words[2], @CX
        y_ref = :cl
      end
      load_value_into_register words[1], register
      emit "#{action_to_mnemonic op} #{register}, #{y_ref}\n"
    when :'auto-bytes'
      auto_bytes words[1], register
    when :'auto-words'
      auto_words words[1], register
    when :call
      call *words[1..-1]
      emit "mov #{register}, #{@RETURN_REG}\n" if register != @RETURN_REG
    when :div
      eval_div words[1], words[2]
      set_register register, @AX
    when :'get-byte'
      # Get address reference
      address_ref = load_address words[1], words[2], 1
      # Load byte from address
      case register
      when :eax, :rax
        set_register register, 0
        set_register :al, address_ref
      when :ebx, :rbx
        set_register register, 0
        set_register :bl, address_ref
      when :ecx, :rcx
        set_register register, 0
        set_register :cl, address_ref
      when :edx, :rdx
        set_register register, 0
        set_register :dl, address_ref
      else
        set_register @AX, 0
        set_register :al, address_ref
        set_register register, @AX
      end
    when :'get-word'
      address_ref = load_address words[1], words[2], @WORDSIZE
      set_register register, address_ref
    when :mod
      eval_div words[1], words[2]
      set_register register, @DX
    when :mul
      eval_mul words[1], words[2], register
    when :not
      load_value_into_register words[1], register
      emit "not #{register}\n"
    else
      if binop?(op)
        with_temporary do |t1|
          x_ref = load_value words[1], @DX
          y_ref = load_value words[2], t1
          if register == y_ref
            emit "mov #{@DX}, #{x_ref}\n"
            emit "#{op} #{@DX}, #{y_ref}\n"
            emit "mov #{register}, #{@DX}\n"
          else
            emit "mov #{register}, #{x_ref}\n" unless register == x_ref
            emit "#{op} #{register}, #{y_ref}\n"
          end
        end
      else
        raise "Not a magic word: #{words[0]}"
      end
    end
  end
end
eval_mul(x, y, register = @AX) click to toggle source

Multiply x by y. The result is stored in @AX by default, but a different register can be specified by passing a third argument.

# File voodoo/generators/nasm_generator.rb, line 603
def eval_mul x, y, register = @AX
  with_temporary do |t1|
    x_ref = load_value x, @DX
    y_ref = load_value y, t1

    if immediate_operand? x_ref
      if immediate_operand? y_ref
        set_register register, x_ref * y_ref
      else
        emit "imul #{register}, #{y_ref}, #{x_ref}\n"
      end
    elsif immediate_operand? y_ref
      emit "imul #{register}, #{x_ref}, #{y_ref}\n"
    elsif y_ref != register
      emit "mov #{register}, #{x_ref}\n" unless x_ref == register
      emit "imul #{register}, #{y_ref}\n"
    else
      emit "imul #{register}, #{x_ref}\n"
    end
  end
end
goto(value) click to toggle source

Continue execution at the given address

# File voodoo/generators/nasm_generator.rb, line 48
def goto value
  with_temporary do |temporary|
    value_ref = load_value value, temporary
    emit "jmp #{value_ref}\n"
  end
end
ifelse() click to toggle source

Start the false path of a conditional.

# File voodoo/generators/nasm_generator.rb, line 682
def ifelse
  newlabel = @environment.gensym
  emit "jmp #{newlabel}\n"
  label = @if_labels.pop
  emit "#{label}:\n"
  @if_labels.push newlabel
end
ifeq(x, y) click to toggle source

Test if x is equal to y

# File voodoo/generators/nasm_generator.rb, line 691
def ifeq x, y
  common_if :je, x, y
end
ifge(x, y) click to toggle source

Test if x is greater than or equal to y

# File voodoo/generators/nasm_generator.rb, line 696
def ifge x, y
  common_if :jge, x, y
end
ifgt(x, y) click to toggle source

Test if x is strictly greater than y

# File voodoo/generators/nasm_generator.rb, line 701
def ifgt x, y
  common_if :jg, x, y
end
ifle(x, y) click to toggle source

Test if x is less than or equal to y

# File voodoo/generators/nasm_generator.rb, line 706
def ifle x, y
  common_if :jle, x, y
end
iflt(x, y) click to toggle source

Test if x is strictly less than y

# File voodoo/generators/nasm_generator.rb, line 711
def iflt x, y
  common_if :jl, x, y
end
ifne(x, y) click to toggle source

Test if x different from y

# File voodoo/generators/nasm_generator.rb, line 716
def ifne x, y
  common_if :jne, x, y
end
immediate_operand?(operand) click to toggle source

Tests if an operand is an immediate operand

# File voodoo/generators/nasm_generator.rb, line 261
def immediate_operand? operand
  integer?(operand)
end
let(symbol, *words) click to toggle source

Introduces a new local variable.

# File voodoo/generators/nasm_generator.rb, line 240
def let symbol, *words
  loc = local_offset_or_register @environment.locals
  @environment.add_local symbol, loc
  set symbol, *words
end
load_address(base, offset, scale) click to toggle source

Create a value reference to an address. Invoking this code may use a temporary and/or clobber @CX

# File voodoo/generators/nasm_generator.rb, line 285
def load_address base, offset, scale
  with_temporary do |temporary|
    base_ref = load_value base, temporary
    offset_ref = load_value offset, @CX

    if offset_ref == 0
      if integer? base_ref
        # Only an integer base
        "[#{base_ref}]"
      else
        # Some complex base; load in temporary
        emit "mov #{temporary}, #{base_ref}\n"
        "[#{temporary}]"
      end
    elsif base_ref == 0
      if integer? offset_ref
        # Only a scaled offset
        "[#{offset_ref.to_i * scale}]"
      else
        # Some complex offset; load in @CX
        emit "mov #{@CX}, #{offset_ref}\n"
        "[#{@CX} * #{scale}]"
      end
    elsif integer? base_ref
      if integer? offset_ref
        # All integers, combine them together
        "[#{base_ref.to_i + (offset_ref.to_i * scale)}]"
      else
        # Complex offset; use @CX
        emit "mov #{@CX}, #{offset_ref}\n"
        "[#{base_ref} + #{@CX} * #{scale}]"
      end
    elsif integer? offset_ref
      # Complex base, integer offset; use temporary
      emit "mov #{temporary}, #{base_ref}\n"
      "[#{temporary} + #{offset_ref.to_i * scale}]"
    else
      # Both base and offset are complex
      # Use both temporary and @CX
      emit "mov #{temporary}, #{base_ref}\n"
      emit "mov #{@CX}, #{offset_ref}\n"
      "[#{temporary} + #{@CX} * #{scale}]"
    end
  end
end
load_at(address, reg) click to toggle source

Load the value at the given address.

# File voodoo/generators/nasm_generator.rb, line 332
def load_at address, reg
  if integer?(address)
    "[#{address}]"
  else
    load_value_into_register address, reg
    "[#{reg}]"
  end
end
load_symbol(symbol, reg) click to toggle source

Loads the value associated with the given symbol.

# File voodoo/generators/nasm_generator.rb, line 342
def load_symbol symbol, reg
  x = @environment[symbol]
  if x.kind_of? Symbol
    x
  elsif x.kind_of? Integer
    offset_reference x
  else
    # Assume global
    @symbol_tracker.use symbol
    if @relocated_symbols.include? symbol
      load_symbol_from_got symbol, reg
    else
      symbol
    end
  end
end
load_value(value, reg) click to toggle source

Loads a value. Returns a string that can be used to refer to the loaded value.

# File voodoo/generators/nasm_generator.rb, line 361
def load_value value, reg
  if substitution? value
    value = substitute_number value[1]
  end

  if integer? value
    if @WORDSIZE > 4 && (value < -2147483648 || value > 2147483647)
      # AMD64 can load immediate values that are outside the range
      # that can be represented as a 32-bit signed integer, but
      # only with a mov instruction that loads the value into a
      # register.
      emit "mov #{@WORD_NAME} #{reg}, #{value}\n"
      reg
    else
      value
    end
  elsif symbol? value
    load_symbol value, reg
  elsif at_expr? value
    load_at value[1], reg
  else
    raise "Don't know how to load #{value.inspect}"
  end
end
load_value_into_register(value, register) click to toggle source

Loads a value into a register.

# File voodoo/generators/nasm_generator.rb, line 387
def load_value_into_register value, register
  value_ref = load_value value, register
  set_register register, value_ref
end
memory_operand?(operand) click to toggle source

Tests if an operand is a memory operand

# File voodoo/generators/nasm_generator.rb, line 266
def memory_operand? operand
  operand.kind_of?(String) && operand[0] == [[
end
mod(target, x, y) click to toggle source

Divide x by y and store the remainder in target

# File voodoo/generators/nasm_generator.rb, line 465
def mod target, x, y
  eval_div x, y
  with_temporary do |temporary|
    target_ref = load_value target, temporary
    emit "mov #{target_ref}, #{@DX}\n"
  end
end
mod2(target, x) click to toggle source

Divide target by x and store the remainder in target

# File voodoo/generators/nasm_generator.rb, line 474
def mod2 target, x
  mod target, target, x
end
mul(target, x, y) click to toggle source

Multiply x by y and store the result in target

# File voodoo/generators/nasm_generator.rb, line 479
def mul target, x, y
  eval_mul x, y
  with_temporary do |temporary|
    target_ref = load_value target, temporary
    emit "mov #{target_ref}, #{@RETURN_REG}\n"
  end
end
mul2(target, x) click to toggle source

Multiply target by x and store the result in target

# File voodoo/generators/nasm_generator.rb, line 488
def mul2 target, x
  mul target, target, x
end
offset_reference(offset) click to toggle source

Returns a memory reference for the address at the given offset from the frame pointer.

# File voodoo/generators/nasm_generator.rb, line 743
def offset_reference offset
  if offset > 0
    "[#{@BP} + #{offset}]"
  elsif offset < 0
    "[#{@BP} - #{-offset}]"
  else
    "[#{@BP}]"
  end
end
qword(value) click to toggle source

Define a qword with the given value

# File voodoo/generators/nasm_generator.rb, line 88
def qword value
  emit "dq #{value}\n"
end
ret(*words) click to toggle source

Returns from a function.

words may contain an expression to be evaluated. The result of the evaluation is returned from the function.

# File voodoo/generators/nasm_generator.rb, line 149
def ret *words
  eval_expr(words) unless words.empty?
  emit_function_epilogue
  emit "ret\n"
end
set(target, *words) click to toggle source

Evaluate the expr in words and store the result in target

# File voodoo/generators/nasm_generator.rb, line 406
def set target, *words
  if integer? target
    raise "Cannot change value of integer #{target}"
  elsif global?(target)
    raise "Cannot change value of global #{target}"
  end

  if words.length != 1 || words[0] != target
    if symbol?(target) && symbol?(@environment[target])
      eval_expr words, @environment[target]
    else
      eval_expr words, @RETURN_REG
      with_temporary do |temporary|
        target_ref = load_value target, temporary
        emit "mov #{target_ref}, #{@RETURN_REG}\n"
      end
    end
  end
end
set_byte(base, offset, value) click to toggle source

Set the byte at base + offset to value

# File voodoo/generators/nasm_generator.rb, line 427
def set_byte base, offset, value
  if immediate_operand?(value)
    value_ref = value
  else
    load_value_into_register value, @RETURN_REG
    value_ref = :al
  end
  addr_ref = load_address base, offset, 1
  emit "mov byte #{addr_ref}, #{value_ref}\n"
end
set_register(register, value_ref) click to toggle source

Set a register to a value. The value must be a valid operand to the mov instruction.

# File voodoo/generators/nasm_generator.rb, line 755
def set_register register, value_ref
  case value_ref
  when register
    # Nothing to do
  when 0
    emit "xor #{register}, #{register}\n"
  else
    emit "mov #{register}, #{value_ref}\n"
  end
end
set_word(base, offset, value) click to toggle source

Set the word at base + offset * +@WORDSIZE+ to value

# File voodoo/generators/nasm_generator.rb, line 439
def set_word base, offset, value
  if immediate_operand?(value)
    value_ref = load_value value, @RETURN_REG
  else
    load_value_into_register value, @RETURN_REG
    value_ref = @RETURN_REG
  end
  addr_ref = load_address base, offset, @WORDSIZE
  emit "mov #{@WORD_NAME} #{addr_ref}, #{value_ref}\n"
end
string(value) click to toggle source

Define a string with the given value

# File voodoo/generators/nasm_generator.rb, line 93
def string value
  code = ''
  in_quote = false
  value.each_byte do |b|
    if b >= 32 && b < 127 && b != 39 
      if in_quote
        code << b.chr
      else
        code << ',' unless code.empty?
        code << "'" + b.chr
        in_quote = true
      end
    else
      if in_quote
        code << "',#{b}"
        in_quote = false
      else
        code << ',' unless code.empty?
        code << "#{b}"
      end
    end
  end
  code << "'" if in_quote
  emit "db #{code}\n"
end
write(io) click to toggle source

Write generated code to the given IO object.

# File voodoo/generators/nasm_generator.rb, line 771
def write io
  io.puts "bits #{@WORDSIZE * 8}\n\n"
  @sections.each do |section,code|
    unless code.empty?
      io.puts "section #{section.to_s}"
      io.puts code
      io.puts
    end
  end
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.