remove trailing whitespace

This commit is contained in:
Sami Samhuri 2014-02-22 11:53:38 -08:00
parent 87672a1a00
commit 8013b8796c
17 changed files with 243 additions and 243 deletions

View file

@ -1,7 +1,7 @@
# Assembler container module. Sub modules are Text and Binary, which # Assembler container module. Sub modules are Text and Binary, which
# both export the same interface for generating either assembly or # both export the same interface for generating either assembly or
# machine code for x86. # machine code for x86.
# #
# sjs # sjs
# may 2009 # may 2009

View file

@ -1,7 +1,7 @@
# A very basic x86 assembler library for Ruby. Generally the # A very basic x86 assembler library for Ruby. Generally the
# instructions implemented are the minimum needed by the compiler this # instructions implemented are the minimum needed by the compiler this
# is written for. x86 is just too big. # is written for. x86 is just too big.
# #
# sjs # sjs
# may 2009 # may 2009
# #
@ -23,7 +23,7 @@ module Assembler
DEBUG_OUTPUT = false DEBUG_OUTPUT = false
# 0.size gives the real answer, we only do x86-32 though # 0.size gives the real answer, we only do x86-32 though
MachineBytes = 4 MachineBytes = 4
MachineBits = MachineBytes * 8 MachineBits = MachineBytes * 8
MinSigned = -1 * 2**(MachineBits-1) MinSigned = -1 * 2**(MachineBits-1)
MaxSigned = 2**(MachineBits-1) - 1 MaxSigned = 2**(MachineBits-1) - 1
@ -65,7 +65,7 @@ module Assembler
@symtab = symtab @symtab = symtab
@objwriter_class = objwriter_class @objwriter_class = objwriter_class
# @objwriter = objwriter # @objwriter = objwriter
# Almost a byte array, except for addresses. # Almost a byte array, except for addresses.
# #
# Addresses take the form [:<type>, <name>] # Addresses take the form [:<type>, <name>]
@ -96,7 +96,7 @@ module Assembler
X86_exit[@platform].each {|byte| emit_byte(byte)} X86_exit[@platform].each {|byte| emit_byte(byte)}
byte_array = resolve_labels byte_array = resolve_labels
#puts "1st pass: " + byte_array.inspect if DEBUG_OUTPUT #puts "1st pass: " + byte_array.inspect if DEBUG_OUTPUT
binary = package(byte_array) binary = package(byte_array)
@ -114,7 +114,7 @@ module Assembler
# outline: # outline:
# - resolve all variable proxies in @proxies replacing # - resolve all variable proxies in @proxies replacing
# the 4 bytes (0xff) with the real address # the 4 bytes (0xff) with the real address
bss_offset = @symtab.bss_offset bss_offset = @symtab.bss_offset
const_offset = @symtab.const_offset const_offset = @symtab.const_offset
@proxies.each do |i, proxy| @proxies.each do |i, proxy|
@ -197,7 +197,7 @@ module Assembler
def label?(x) def label?(x)
x.is_a?(Array) && x[0] == :label x.is_a?(Array) && x[0] == :label
end end
# XXX this should probably evaluate the value somehow # XXX this should probably evaluate the value somehow
def defconst(name, bytes, value) def defconst(name, bytes, value)
@symtab.defconst(name, bytes, value) @symtab.defconst(name, bytes, value)
@ -247,18 +247,18 @@ module Assembler
def asm def asm
# stash the current number of bytes written # stash the current number of bytes written
instruction_offset = @ip instruction_offset = @ip
print "0x#{@ip.to_s(16).rjust(4, '0')}\t" if DEBUG_OUTPUT print "0x#{@ip.to_s(16).rjust(4, '0')}\t" if DEBUG_OUTPUT
yield yield
# return the number of bytes written # return the number of bytes written
@ip - instruction_offset @ip - instruction_offset
puts if DEBUG_OUTPUT puts if DEBUG_OUTPUT
end end
def emit_byte(byte) def emit_byte(byte)
##### The joke's on me! Array#pack('c*') already does this. It is nice to see ##### The joke's on me! Array#pack('c*') already does this. It is nice to see
@ -276,12 +276,12 @@ module Assembler
# make sure it's a byte # make sure it's a byte
raise "not a byte: #{byte.inspect}" unless byte == byte & 0xff raise "not a byte: #{byte.inspect}" unless byte == byte & 0xff
byte = byte & 0xff byte = byte & 0xff
### end of pointless code ### end of pointless code
print (byte >= 0 && byte < 0x10 ? '0' : '') + byte.to_s(16) + ' ' if DEBUG_OUTPUT print (byte >= 0 && byte < 0x10 ? '0' : '') + byte.to_s(16) + ' ' if DEBUG_OUTPUT
@ir << byte @ir << byte
@ip += 1 @ip += 1
end end
@ -314,7 +314,7 @@ module Assembler
def emit_dword(num) def emit_dword(num)
num_to_quad(num).each { |byte| emit_byte(byte) } num_to_quad(num).each { |byte| emit_byte(byte) }
end end
def mklabel(suffix=nil) def mklabel(suffix=nil)
@symtab.unique_label(suffix) @symtab.unique_label(suffix)
end end
@ -372,7 +372,7 @@ module Assembler
mod = 1 mod = 1
disp8 = eff_addr.index disp8 = eff_addr.index
# disp32, mod == 10 # disp32, mod == 10
elsif SignedRange === eff_addr.index elsif SignedRange === eff_addr.index
mod = 2 mod = 2
disp32 = eff_addr.index disp32 = eff_addr.index
@ -501,7 +501,7 @@ module Assembler
def log2(x, tol=1e-13) def log2(x, tol=1e-13)
result = 0.0 result = 0.0
# Integer part # Integer part
while x < 1 while x < 1
resultp -= 1 resultp -= 1
@ -511,7 +511,7 @@ module Assembler
result += 1 result += 1
x /= 2 x /= 2
end end
# Fractional part # Fractional part
fp = 1.0 fp = 1.0
while fp >= tol while fp >= tol
@ -521,7 +521,7 @@ module Assembler
x /= 2 x /= 2
result += fp result += fp
end end
end end
result result
end end
@ -537,14 +537,14 @@ module Assembler
# 6. mov reg8, r/m8 # 6. mov reg8, r/m8
# 7. mov r/m8, imm8 # 7. mov r/m8, imm8
def mov(dest, src) def mov(dest, src)
# These 2 are used in the same way, just the name differs to make the # These 2 are used in the same way, just the name differs to make the
# meaning clear. They are 4-byte values that are emited at the end if # meaning clear. They are 4-byte values that are emited at the end if
# they are non-nil. Only one of them will be emited, and if both are # they are non-nil. Only one of them will be emited, and if both are
# non-nil that one is immediate. # non-nil that one is immediate.
immediate = nil immediate = nil
offset = nil offset = nil
# This is an array of arguments to be passed to emit_modrm, if it is set. # This is an array of arguments to be passed to emit_modrm, if it is set.
modrm = nil modrm = nil
@ -580,7 +580,7 @@ module Assembler
opcode = 0xc7 opcode = 0xc7
modrm = [dest, 0] modrm = [dest, 0]
immediate = src immediate = src
# version 5: mov r/m8, r8 # version 5: mov r/m8, r8
elsif rm?(dest, :byte) && register?(src, :byte) elsif rm?(dest, :byte) && register?(src, :byte)
opcode = 0x88 opcode = 0x88
@ -627,12 +627,12 @@ module Assembler
emit_byte(immediate_byte) emit_byte(immediate_byte)
end end
end end
end end
def movzx(dest, src) def movzx(dest, src)
# movzx Gv, ?? # movzx Gv, ??
if register?(dest) if register?(dest)
@ -648,10 +648,10 @@ module Assembler
emit_byte(0x0f) emit_byte(0x0f)
emit_byte(opcode) emit_byte(opcode)
emit_modrm(src, dest.regnum) emit_modrm(src, dest.regnum)
end end
else else
raise "unimplemented MOVZX instruction, << dest=#{dest.inspect} >> src=#{src.inspect}" raise "unimplemented MOVZX instruction, << dest=#{dest.inspect} >> src=#{src.inspect}"
end end
end end
@ -700,21 +700,21 @@ module Assembler
emit_modrm(dest, 0) emit_modrm(dest, 0)
emit_dword(src) emit_dword(src)
end end
# add eax, imm32 # add eax, imm32
elsif dest == EAX && immediate?(src) elsif dest == EAX && immediate?(src)
asm do asm do
emit_byte(0x05) emit_byte(0x05)
emit_dword(src) emit_dword(src)
end end
# add reg32, r/m32 # add reg32, r/m32
elsif register?(dest) && rm?(src) elsif register?(dest) && rm?(src)
asm do asm do
emit_byte(0x03) emit_byte(0x03)
emit_modrm(src, dest.regnum) emit_modrm(src, dest.regnum)
end end
else else
raise "unsupported ADD instruction, dest=#{dest.inspect} src=#{src.inspect}" raise "unsupported ADD instruction, dest=#{dest.inspect} src=#{src.inspect}"
end end
@ -729,7 +729,7 @@ module Assembler
emit_modrm(dest, 5) emit_modrm(dest, 5)
emit_byte(src) emit_byte(src)
end end
# sub r/m32, imm32 # sub r/m32, imm32
elsif rm?(dest) && immediate?(src) elsif rm?(dest) && immediate?(src)
asm do asm do
@ -737,7 +737,7 @@ module Assembler
emit_modrm(dest, 5) emit_modrm(dest, 5)
emit_dword(src) emit_dword(src)
end end
# sub r/m32, reg32 # sub r/m32, reg32
elsif rm?(dest) && register?(src) elsif rm?(dest) && register?(src)
asm do asm do
@ -803,8 +803,8 @@ module Assembler
end end
end end
end end
def dec(op) def dec(op)
if register?(op) if register?(op)
# dec reg32 # dec reg32
@ -827,7 +827,7 @@ module Assembler
emit_modrm(op, 5) emit_modrm(op, 5)
emit_byte(n) emit_byte(n)
end end
else else
raise "unsupported SHR instruction, op=#{op.inspect}, n=#{n.inspect}" raise "unsupported SHR instruction, op=#{op.inspect}, n=#{n.inspect}"
end end
@ -878,18 +878,18 @@ module Assembler
emit_byte(0x31) emit_byte(0x31)
emit_modrm(dest, src.regnum) emit_modrm(dest, src.regnum)
end end
else else
raise "unsupported XOR instruction, dest=#{dest.inspect} src=#{src.inspect}" raise "unsupported XOR instruction, dest=#{dest.inspect} src=#{src.inspect}"
end end
end end
def not_(op) def not_(op)
group3(op, 2, 'NOT') group3(op, 2, 'NOT')
end end
alias_method :not, :not_ alias_method :not, :not_
def neg(op) def neg(op)
group3(op, 3, 'NEG') group3(op, 3, 'NEG')
@ -900,19 +900,19 @@ module Assembler
# push reg32 # push reg32
if register?(op) if register?(op)
asm { emit_byte(0x50 + op.regnum) } asm { emit_byte(0x50 + op.regnum) }
elsif immediate?(op, :byte) elsif immediate?(op, :byte)
asm do asm do
emit_byte(0x6a) emit_byte(0x6a)
emit_byte(op) emit_byte(op)
end end
elsif immediate?(op) elsif immediate?(op)
asm do asm do
emit_byte(0x68) emit_byte(0x68)
emit_dword(op) emit_dword(op)
end end
else else
raise "unsupported PUSH instruction: op=#{op.inspect}" raise "unsupported PUSH instruction: op=#{op.inspect}"
end end
@ -923,7 +923,7 @@ module Assembler
# pop reg32 # pop reg32
if register?(op) if register?(op)
asm { emit_byte(0x58 + op.regnum) } asm { emit_byte(0x58 + op.regnum) }
else else
raise "unsupported POP instruction: op=#{op.inspect}" raise "unsupported POP instruction: op=#{op.inspect}"
end end
@ -937,14 +937,14 @@ module Assembler
emit_byte(0x39) emit_byte(0x39)
emit_modrm(op1, op2.regnum) emit_modrm(op1, op2.regnum)
end end
# cmp eax, imm32 # cmp eax, imm32
elsif op1 == EAX && immediate?(op2) elsif op1 == EAX && immediate?(op2)
asm do asm do
emit_byte(0x3d) emit_byte(0x3d)
emit_dword(op2) emit_dword(op2)
end end
else else
raise "unsupported CMP instruction: op1=#{op1.inspect} op2=#{op2.inspect}" raise "unsupported CMP instruction: op1=#{op1.inspect} op2=#{op2.inspect}"
end end
@ -977,7 +977,7 @@ module Assembler
# Only Jcc rel32 is supported. # Only Jcc rel32 is supported.
def jcc(instruction, label) def jcc(instruction, label)
opcode = JccOpcodeMap[instruction] opcode = JccOpcodeMap[instruction]
asm do asm do
emit_byte(0x0f) emit_byte(0x0f)
emit_byte(opcode) emit_byte(opcode)
@ -1025,7 +1025,7 @@ module Assembler
unless SignedByte === delta unless SignedByte === delta
raise "LOOP can only jump -128 to 127 bytes, #{label} is #{delta} bytes away" raise "LOOP can only jump -128 to 127 bytes, #{label} is #{delta} bytes away"
end end
asm do asm do
emit_byte(0xe2) emit_byte(0xe2)
emit_byte(delta) emit_byte(delta)
@ -1035,10 +1035,10 @@ module Assembler
# Opcode group #3. 1-byte opcode, 1 operand (r/m8 or r/m32). # Opcode group #3. 1-byte opcode, 1 operand (r/m8 or r/m32).
# #
# Members of this group are: DIV, IDIV, MUL, IMUL, NEG, NOT, and TEST. # Members of this group are: DIV, IDIV, MUL, IMUL, NEG, NOT, and TEST.
def group3(op, reg, instruction) def group3(op, reg, instruction)
opcode = opcode =
if rm?(op, 8) if rm?(op, 8)
0xf6 0xf6
elsif rm?(op) elsif rm?(op)
@ -1055,5 +1055,5 @@ module Assembler
end # class Binary end # class Binary
end # module Assembler end # module Assembler

View file

@ -4,7 +4,7 @@
# subclassed just like any other class. A nice side-effect of this # subclassed just like any other class. A nice side-effect of this
# syntax is that it is always clear that a CStruct is just a class and # syntax is that it is always clear that a CStruct is just a class and
# instances of the struct are objects. # instances of the struct are objects.
# #
# Some light metaprogramming is used to make the following syntax possible: # Some light metaprogramming is used to make the following syntax possible:
# #
# class MachHeader < CStruct # class MachHeader < CStruct
@ -21,12 +21,12 @@
# uint32 :cmd # uint32 :cmd
# uint32 :cmdsize # uint32 :cmdsize
# end # end
# #
# # inherits cmd and cmdsize as the first 2 fields # # inherits cmd and cmdsize as the first 2 fields
# class SegmentCommand < LoadCommand # class SegmentCommand < LoadCommand
# string :segname, 16 # string :segname, 16
# uint32 :vmaddr # uint32 :vmaddr
# uint32 # uint32
# end # end
# #
# Nothing tricky or confusing there. Members of a CStruct class are # Nothing tricky or confusing there. Members of a CStruct class are
@ -56,7 +56,7 @@ class CStruct
################### ###################
# Class Constants # # Class Constants #
################### ###################
# Size in bytes. # Size in bytes.
SizeMap = { SizeMap = {
:int8 => 1, :int8 => 1,
@ -89,7 +89,7 @@ class CStruct
:uint => 'I', :uint => 'I',
:char => 'C' :char => 'C'
} }
# Only needed when unpacking is different from packing, i.e. strings w/ lambdas in PackMap. # Only needed when unpacking is different from packing, i.e. strings w/ lambdas in PackMap.
UnpackMap = { UnpackMap = {
:string => lambda do |str, *opts| :string => lambda do |str, *opts|
@ -99,11 +99,11 @@ class CStruct
val val
end end
} }
########################## ##########################
# Class Instance Methods # # Class Instance Methods #
########################## ##########################
# Note: const_get and const_set are used so the constants are bound # Note: const_get and const_set are used so the constants are bound
# at runtime, to the real class that has subclassed CStruct. # at runtime, to the real class that has subclassed CStruct.
# I figured Ruby would do this but I haven't looked at the # I figured Ruby would do this but I haven't looked at the
@ -112,31 +112,31 @@ class CStruct
# All of this could probably be avoided with Ruby 1.9 and # All of this could probably be avoided with Ruby 1.9 and
# private class variables. That is definitely something to # private class variables. That is definitely something to
# experiment with. # experiment with.
class <<self class <<self
def inherited(subclass) def inherited(subclass)
subclass.instance_eval do subclass.instance_eval do
# These "constants" are only constant references. Structs can # These "constants" are only constant references. Structs can
# be modified. After the struct is defined it is still open, # be modified. After the struct is defined it is still open,
# but good practice would be not to change a struct after it # but good practice would be not to change a struct after it
# has been defined. # has been defined.
# #
# To support inheritance properly we try to get these # To support inheritance properly we try to get these
# constants from the enclosing scope (and clone them before # constants from the enclosing scope (and clone them before
# modifying them!), and default to empty, er, defaults. # modifying them!), and default to empty, er, defaults.
members = const_get(:Members).clone rescue [] members = const_get(:Members).clone rescue []
member_index = const_get(:MemberIndex).clone rescue {} member_index = const_get(:MemberIndex).clone rescue {}
member_sizes = const_get(:MemberSizes).clone rescue {} member_sizes = const_get(:MemberSizes).clone rescue {}
member_opts = const_get(:MemberOptions).clone rescue {} member_opts = const_get(:MemberOptions).clone rescue {}
const_set(:Members, members) const_set(:Members, members)
const_set(:MemberIndex, member_index) const_set(:MemberIndex, member_index)
const_set(:MemberSizes, member_sizes) const_set(:MemberSizes, member_sizes)
const_set(:MemberOptions, member_opts) const_set(:MemberOptions, member_opts)
end end
end end
@ -144,7 +144,7 @@ class CStruct
# Define a method for each size name, and when that method is called it updates # Define a method for each size name, and when that method is called it updates
# the struct class accordingly. # the struct class accordingly.
SizeMap.keys.each do |type| SizeMap.keys.each do |type|
define_method(type) do |name, *args| define_method(type) do |name, *args|
name = name.to_sym name = name.to_sym
const_get(:MemberIndex)[name] = const_get(:Members).size const_get(:MemberIndex)[name] = const_get(:Members).size
@ -152,21 +152,21 @@ class CStruct
const_get(:MemberOptions)[name] = args const_get(:MemberOptions)[name] = args
const_get(:Members) << name const_get(:Members) << name
end end
end end
# Return the number of members. # Return the number of members.
def size def size
const_get(:Members).size const_get(:Members).size
end end
alias_method :length, :size alias_method :length, :size
# Return the number of bytes occupied in memory or on disk. # Return the number of bytes occupied in memory or on disk.
def bytesize def bytesize
const_get(:Members).inject(0) { |size, name| size + sizeof(name) } const_get(:Members).inject(0) { |size, name| size + sizeof(name) }
end end
def sizeof(name) def sizeof(name)
value = SizeMap[const_get(:MemberSizes)[name]] value = SizeMap[const_get(:MemberSizes)[name]]
value.respond_to?(:call) ? value.call(*const_get(:MemberOptions)[name]) : value value.respond_to?(:call) ? value.call(*const_get(:MemberOptions)[name]) : value
@ -178,14 +178,14 @@ class CStruct
end end
end end
#################### ####################
# Instance Methods # # Instance Methods #
#################### ####################
attr_reader :values attr_reader :values
def initialize(*args) def initialize(*args)
@values = args @values = args
end end
@ -199,7 +199,7 @@ class CStruct
[vals.shift].pack(patt) [vals.shift].pack(patt)
else else
patt.call(vals.shift, *member_options[name]) patt.call(vals.shift, *member_options[name])
end end
end.join end.join
end end
@ -218,47 +218,47 @@ class CStruct
end end
self self
end end
def pack_pattern def pack_pattern
members.map { |name| PackMap[member_sizes[name]] } members.map { |name| PackMap[member_sizes[name]] }
end end
def unpack_pattern def unpack_pattern
members.map { |name| UnpackMap[member_sizes[name]] || PackMap[member_sizes[name]] } members.map { |name| UnpackMap[member_sizes[name]] || PackMap[member_sizes[name]] }
end end
def [](name_or_idx) def [](name_or_idx)
case name_or_idx case name_or_idx
when Numeric when Numeric
idx = name_or_idx idx = name_or_idx
@values[idx] @values[idx]
when String, Symbol when String, Symbol
name = name_or_idx.to_sym name = name_or_idx.to_sym
@values[member_index[name]] @values[member_index[name]]
else else
raise ArgumentError, "expected name or index, got #{name_or_idx.inspect}" raise ArgumentError, "expected name or index, got #{name_or_idx.inspect}"
end end
end end
def []=(name_or_idx, value) def []=(name_or_idx, value)
case name_or_idx case name_or_idx
when Numeric when Numeric
idx = name_or_idx idx = name_or_idx
@values[idx] = value @values[idx] = value
when String, Symbol when String, Symbol
name = name_or_idx.to_sym name = name_or_idx.to_sym
@values[member_index[name]] = value @values[member_index[name]] = value
else else
raise ArgumentError, "expected name or index, got #{name_or_idx.inspect}" raise ArgumentError, "expected name or index, got #{name_or_idx.inspect}"
end end
end end
def ==(other) def ==(other)
puts @values.inspect puts @values.inspect
puts other.values.inspect puts other.values.inspect
@ -270,16 +270,16 @@ class CStruct
def each(&block) def each(&block)
@values.each(&block) @values.each(&block)
end end
def each_pair(&block) def each_pair(&block)
members.zip(@values).each(&block) members.zip(@values).each(&block)
end end
def size def size
members.size members.size
end end
alias_method :length, :size alias_method :length, :size
def sizeof(name) def sizeof(name)
self.class.sizeof(name) self.class.sizeof(name)
end end
@ -296,21 +296,21 @@ class CStruct
def members def members
self.class::Members self.class::Members
end end
def member_index def member_index
self.class::MemberIndex self.class::MemberIndex
end end
def member_sizes def member_sizes
self.class::MemberSizes self.class::MemberSizes
end end
def member_options def member_options
self.class::MemberOptions self.class::MemberOptions
end end
# The last expression is returned, so return self instead of junk. # The last expression is returned, so return self instead of junk.
self self
end end

View file

@ -1,7 +1,7 @@
module Assembler module Assembler
class ELFSymtab < Symtab class ELFSymtab < Symtab
end end
end end

View file

@ -1,9 +1,9 @@
module Assembler module Assembler
class ELFWriter < ObjWriter class ELFWriter < ObjWriter
end end
end end

View file

@ -2,7 +2,7 @@ require 'asm/cstruct'
# The MachO module contains constants and structures related to the # The MachO module contains constants and structures related to the
# Mach Object format (Mach-O). They are relevant to Darwin on OS X. # Mach Object format (Mach-O). They are relevant to Darwin on OS X.
# #
# Constants and structures as defined in /usr/include/mach-o/loader.h # Constants and structures as defined in /usr/include/mach-o/loader.h
# on Mac OS X Leopard (10.5.7). Also see <mach-o/stab.h>, # on Mac OS X Leopard (10.5.7). Also see <mach-o/stab.h>,
# <mach-o/nlist.h>, and <mach-o/reloc.h>. # <mach-o/nlist.h>, and <mach-o/reloc.h>.
@ -13,7 +13,7 @@ module MachO
############### ###############
# Mach header # # Mach header #
############### ###############
# Appears at the beginning of every Mach object file. # Appears at the beginning of every Mach object file.
class MachHeader < CStruct class MachHeader < CStruct
uint32 :magic uint32 :magic
@ -22,7 +22,7 @@ module MachO
uint32 :filetype uint32 :filetype
uint32 :ncmds uint32 :ncmds
uint32 :sizeofcmds uint32 :sizeofcmds
uint32 :flags uint32 :flags
end end
# Values for the magic field. # Values for the magic field.
@ -40,13 +40,13 @@ module MachO
MH_BUNDLE = 0x8 MH_BUNDLE = 0x8
MH_DYLIB_STUB = 0x9 MH_DYLIB_STUB = 0x9
MH_DSYM = 0xa MH_DSYM = 0xa
# CPU types and subtypes (only Intel for now). # CPU types and subtypes (only Intel for now).
CPU_TYPE_X86 = 7 CPU_TYPE_X86 = 7
CPU_TYPE_I386 = CPU_TYPE_X86 CPU_TYPE_I386 = CPU_TYPE_X86
CPU_SUBTYPE_X86_ALL = 3 CPU_SUBTYPE_X86_ALL = 3
############################ ############################
# Load commands / segments # # Load commands / segments #
############################ ############################
@ -61,7 +61,7 @@ module MachO
LC_SYMTAB = 0x2 LC_SYMTAB = 0x2
LC_SYMSEG = 0x3 LC_SYMSEG = 0x3
LC_THREAD = 0x4 LC_THREAD = 0x4
LC_UNIXTHREAD = 0x5 LC_UNIXTHREAD = 0x5
class SegmentCommand < LoadCommand class SegmentCommand < LoadCommand
string :segname, 16 string :segname, 16
@ -74,7 +74,7 @@ module MachO
uint32 :nsects uint32 :nsects
uint32 :flags uint32 :flags
end end
# Values for protection fields, maxprot and initprot. # Values for protection fields, maxprot and initprot.
VM_PROT_NONE = 0x00 VM_PROT_NONE = 0x00
@ -91,18 +91,18 @@ module MachO
uint32 :stroff # Offset of the string table. uint32 :stroff # Offset of the string table.
uint32 :strsize # Size of the string table in bytes. uint32 :strsize # Size of the string table in bytes.
end end
LoadCommandStructMap = { LoadCommandStructMap = {
LC_SEGMENT => SegmentCommand, LC_SEGMENT => SegmentCommand,
LC_SYMTAB => SymtabCommand LC_SYMTAB => SymtabCommand
} }
############ ############
# Sections # # Sections #
############ ############
class Section < CStruct class Section < CStruct
string :sectname, 16 string :sectname, 16
string :segname, 16 string :segname, 16
@ -116,18 +116,18 @@ module MachO
uint32 :reserved1 uint32 :reserved1
uint32 :reserved2 uint32 :reserved2
end end
# Values for the type bitfield (mask 0x000000ff) of the flags field. # Values for the type bitfield (mask 0x000000ff) of the flags field.
# (incomplete!) # (incomplete!)
S_REGULAR = 0x0 S_REGULAR = 0x0
S_ZEROFILL = 0x1 S_ZEROFILL = 0x1
S_CSTRING_LITERALS = 0x2 S_CSTRING_LITERALS = 0x2
########################### ###########################
# Relocation info support # # Relocation info support #
########################### ###########################
class RelocationInfo < CStruct class RelocationInfo < CStruct
int32 :r_address # offset in the section to what is being relocated int32 :r_address # offset in the section to what is being relocated
uint32 :r_info uint32 :r_info
@ -149,12 +149,12 @@ module MachO
# Relocation types (r_type) # Relocation types (r_type)
GENERIC_RELOC_VANILLA = 0 GENERIC_RELOC_VANILLA = 0
######################## ########################
# Symbol table support # # Symbol table support #
######################## ########################
# Nlist is used to describe symbols. # Nlist is used to describe symbols.
class Nlist < CStruct class Nlist < CStruct
uint32 :n_strx # Index into string table. Index of zero is the empty string. uint32 :n_strx # Index into string table. Index of zero is the empty string.
@ -163,7 +163,7 @@ module MachO
uint16 :n_desc # TODO See <mach-o/stab.h>. uint16 :n_desc # TODO See <mach-o/stab.h>.
uint32 :n_value # The symbol's value (or stab offset). uint32 :n_value # The symbol's value (or stab offset).
end end
# Type flag (see <mach-o/nlist.h> for more details) # Type flag (see <mach-o/nlist.h> for more details)
# --------- # ---------
# #
@ -178,13 +178,13 @@ module MachO
N_PEXT = 0x10 # private external symbol bit N_PEXT = 0x10 # private external symbol bit
N_TYPE = 0x0e # mask for the type bits N_TYPE = 0x0e # mask for the type bits
N_EXT = 0x01 # external symbol bit, set for external symbols (e.g. globals) N_EXT = 0x01 # external symbol bit, set for external symbols (e.g. globals)
# Values for N_TYPE. (incomplete!) # Values for N_TYPE. (incomplete!)
N_UNDF = 0x0 # undefined, n_sect == NO_SECT N_UNDF = 0x0 # undefined, n_sect == NO_SECT
N_ABS = 0x2 # absolute, n_sect == NO_SECT N_ABS = 0x2 # absolute, n_sect == NO_SECT
N_SECT = 0xe # defined in section number n_sect N_SECT = 0xe # defined in section number n_sect
NO_SECT = 0 NO_SECT = 0
MAX_SECT = 255 MAX_SECT = 255
end end

View file

@ -1,18 +1,18 @@
require 'asm/macho' require 'asm/macho'
module Assembler module Assembler
class MachOFile class MachOFile
include MachO include MachO
attr_accessor :header, :load_commands, :sections, :data attr_accessor :header, :load_commands, :sections, :data
attr_accessor :current_segment attr_accessor :current_segment
def initialize(filetype=MH_OBJECT) def initialize(filetype=MH_OBJECT)
@header = MachHeader.new(MH_MAGIC, CPU_TYPE_X86, CPU_SUBTYPE_X86_ALL, filetype, 0, 0, 0) @header = MachHeader.new(MH_MAGIC, CPU_TYPE_X86, CPU_SUBTYPE_X86_ALL, filetype, 0, 0, 0)
@load_commands = [] # All defined segments. @load_commands = [] # All defined segments.
@sections = {} # Map of segment names to lists of sections. @sections = {} # Map of segment names to lists of sections.
@section_disk_size = Hash.new(0) # Sections store their VM size so we need their sizes on disk. @section_disk_size = Hash.new(0) # Sections store their VM size so we need their sizes on disk.
@section_offset = 0 # Offset of the next section's data, in bytes. @section_offset = 0 # Offset of the next section's data, in bytes.
@data = [] # Blobs of data that appear at the end of the file. @data = [] # Blobs of data that appear at the end of the file.
@ -38,28 +38,28 @@ module Assembler
def load_command(cmdtype) def load_command(cmdtype)
struct = LoadCommandStructMap[cmdtype] struct = LoadCommandStructMap[cmdtype]
unless struct unless struct
raise "unsupported load command type: #{cmdtype.inspect}," + raise "unsupported load command type: #{cmdtype.inspect}," +
" supported types: #{LoadCommandStructMap.keys.sort.inspect}" " supported types: #{LoadCommandStructMap.keys.sort.inspect}"
end end
# Fill in all the unknown fields with 0, this is nonsense for # Fill in all the unknown fields with 0, this is nonsense for
# string fields but that doesn't really matter. # string fields but that doesn't really matter.
dummy_vals = [0] * (struct::Members.size - 2) dummy_vals = [0] * (struct::Members.size - 2)
# cmd cmdsize ... # cmd cmdsize ...
command = struct.new(cmdtype, struct.bytesize, *dummy_vals) command = struct.new(cmdtype, struct.bytesize, *dummy_vals)
@load_commands << command @load_commands << command
@header[:ncmds] += 1 @header[:ncmds] += 1
@header[:sizeofcmds] += command.bytesize @header[:sizeofcmds] += command.bytesize
yield(command) if block_given? yield(command) if block_given?
return command return command
end end
# Define a segment in this file. If a block is given it is passed # Define a segment in this file. If a block is given it is passed
# the new segment. You can chain calls to segment, it returns self. # the new segment. You can chain calls to segment, it returns self.
# #
@ -84,25 +84,25 @@ module Assembler
# them in the segment that they name. # them in the segment that they name.
def section(name, segname, data='', vmsize=data.size, def section(name, segname, data='', vmsize=data.size,
segment=@current_segment, type=S_REGULAR) segment=@current_segment, type=S_REGULAR)
# Create the new section. # Create the new section.
section = Section.new(name, segname, @section_offset, vmsize, 0, 0, 0, 0, 0, 0, type) section = Section.new(name, segname, @section_offset, vmsize, 0, 0, 0, 0, 0, 0, type)
# Add this section to the map of segment names to sections. # Add this section to the map of segment names to sections.
(@sections[segment[:segname]] ||= []) << section (@sections[segment[:segname]] ||= []) << section
@section_disk_size[name] = data.size @section_disk_size[name] = data.size
@section_offset += data.size @section_offset += data.size
@data << data if data.size > 0 @data << data if data.size > 0
# Update the header. # Update the header.
@header[:sizeofcmds] += section.bytesize @header[:sizeofcmds] += section.bytesize
# Update the segment. # Update the segment.
segment[:nsects] += 1 segment[:nsects] += 1
segment[:cmdsize] += section.bytesize segment[:cmdsize] += section.bytesize
yield(section) if block_given? yield(section) if block_given?
return section return section
end end
@ -119,7 +119,7 @@ module Assembler
# #
# For MH_EXECUTE files the text section goes under the segment with the # For MH_EXECUTE files the text section goes under the segment with the
# name given (__TEXT). # name given (__TEXT).
def text(data, sectname='__text', segname='__TEXT') def text(data, sectname='__text', segname='__TEXT')
real_segname = nil real_segname = nil
unless @current_segment unless @current_segment
@ -129,7 +129,7 @@ module Assembler
seg[:initprot] = VM_PROT_READ | VM_PROT_EXECUTE seg[:initprot] = VM_PROT_READ | VM_PROT_EXECUTE
end end
end end
section(sectname, segname, data) do |sect| section(sectname, segname, data) do |sect|
# reloff and nreloc are calculated later (in calculate_offsets) # reloff and nreloc are calculated later (in calculate_offsets)
sect[:flags] = 0x400 # S_ATTR_SOME_INSTRUCTIONS sect[:flags] = 0x400 # S_ATTR_SOME_INSTRUCTIONS
@ -139,7 +139,7 @@ module Assembler
@text_segname = real_segname || segname @text_segname = real_segname || segname
@text_sect_index = @sections[@text_segname].length-1 @text_sect_index = @sections[@text_segname].length-1
@text_data_index = @data.length-1 @text_data_index = @data.length-1
return self return self
end end
@ -203,14 +203,14 @@ module Assembler
@reloc_info = reloc_info.map {|x| x.clone} @reloc_info = reloc_info.map {|x| x.clone}
return self return self
end end
# Define a symbol table. This should usually be placed at the end of the # Define a symbol table. This should usually be placed at the end of the
# file. # file.
# #
# This function is overloaded to accept either an array of Nlist structs # This function is overloaded to accept either an array of Nlist structs
# packed into a byte string (i.e. a C array) and a string table, or a # packed into a byte string (i.e. a C array) and a string table, or a
# single parameter: any type of Symtab. # single parameter: any type of Symtab.
def symtab(nlist_ary_or_symtab, stab=nil) def symtab(nlist_ary_or_symtab, stab=nil)
if stab.nil? if stab.nil?
symtab = nlist_ary_or_symtab symtab = nlist_ary_or_symtab
@ -219,19 +219,19 @@ module Assembler
else else
nlist_ary = nlist_ary_or_symtab nlist_ary = nlist_ary_or_symtab
end end
load_command(LC_SYMTAB) do |st| load_command(LC_SYMTAB) do |st|
st[:nsyms] = nlist_ary.size st[:nsyms] = nlist_ary.size
st[:strsize] = stab.size st[:strsize] = stab.size
# symoff and stroff are filled in when offsets are recalculated. # symoff and stroff are filled in when offsets are recalculated.
end end
# puts ">>> Defining symbol table:" # puts ">>> Defining symbol table:"
# puts ">>> #{nlist_ary.size} symbols" # puts ">>> #{nlist_ary.size} symbols"
# puts ">>> stab = #{stab.inspect}" # puts ">>> stab = #{stab.inspect}"
# puts ">>> nlist_ary = #{nlist_ary.inspect}" # puts ">>> nlist_ary = #{nlist_ary.inspect}"
# puts ">>> (serialized) = #{nlist_ary.map{|n|n.serialize}.join.inspect}" # puts ">>> (serialized) = #{nlist_ary.map{|n|n.serialize}.join.inspect}"
@data << nlist_ary.map {|n| n.serialize}.join @data << nlist_ary.map {|n| n.serialize}.join
@data << stab @data << stab
return self return self
@ -240,11 +240,11 @@ module Assembler
# Serialize the entire MachO file into a byte string. This is simple # Serialize the entire MachO file into a byte string. This is simple
# thanks to CStruct#serialize. # thanks to CStruct#serialize.
def serialize def serialize
# TODO sanity checks, e.g. assert(@header[:ncmds] == @load_command.size) # TODO sanity checks, e.g. assert(@header[:ncmds] == @load_command.size)
# ... perhaps an option to recalculate such data as well. # ... perhaps an option to recalculate such data as well.
# Now that we have all the pieces of the file defined we can calculate # Now that we have all the pieces of the file defined we can calculate
# the file offsets of segments and sections. # the file offsets of segments and sections.
calculate_offsets calculate_offsets
@ -258,7 +258,7 @@ module Assembler
# Mach-O file Part 2: Load Commands # # Mach-O file Part 2: Load Commands #
##################################### #####################################
# dump each load command (which include the section headers under them) # dump each load command (which include the section headers under them)
@load_commands.map do |cmd| @load_commands.map do |cmd|
sects = @sections[cmd[:segname]] rescue [] sects = @sections[cmd[:segname]] rescue []
sects.inject(cmd.serialize) do |data, sect| sects.inject(cmd.serialize) do |data, sect|
data + sect.serialize data + sect.serialize
@ -271,19 +271,19 @@ module Assembler
@data.join @data.join
end end
# Update the file offsets in segments and sections. # Update the file offsets in segments and sections.
def calculate_offsets def calculate_offsets
# Maintain the offset into the the file on disk. This is used # Maintain the offset into the the file on disk. This is used
# to update the various structures. # to update the various structures.
offset = @header.bytesize offset = @header.bytesize
# First pass over load commands. Most sizes are filled in here. # First pass over load commands. Most sizes are filled in here.
@load_commands.each do |cmd| @load_commands.each do |cmd|
case cmd[:cmd] case cmd[:cmd]
when LC_SEGMENT when LC_SEGMENT
seg = cmd seg = cmd
sections = @sections[seg[:segname]] sections = @sections[seg[:segname]]
@ -292,25 +292,25 @@ module Assembler
section_disk_size = sections.inject(0) do |total, sect| section_disk_size = sections.inject(0) do |total, sect|
total + @section_disk_size[sect[:sectname]] total + @section_disk_size[sect[:sectname]]
end end
### TODO this should be redundant. try commenting it out one day. ### TODO this should be redundant. try commenting it out one day.
seg[:nsects] = sections.size seg[:nsects] = sections.size
seg[:cmdsize] = seg.bytesize + section_size seg[:cmdsize] = seg.bytesize + section_size
### ###
seg[:vmsize] = section_vm_size seg[:vmsize] = section_vm_size
seg[:filesize] = section_disk_size seg[:filesize] = section_disk_size
when LC_SYMTAB when LC_SYMTAB
# nop # nop
else else
raise "unsupported load command: #{cmd.inspect}" raise "unsupported load command: #{cmd.inspect}"
end end
offset += cmd[:cmdsize] offset += cmd[:cmdsize]
end end
# offset now points to the end of the Mach-O headers, or the beginning # offset now points to the end of the Mach-O headers, or the beginning
# of the binary blobs of section data at the end. # of the binary blobs of section data at the end.
@ -318,16 +318,16 @@ module Assembler
# Second pass over load commands. Fill in file offsets. # Second pass over load commands. Fill in file offsets.
@load_commands.each do |cmd| @load_commands.each do |cmd|
case cmd[:cmd] case cmd[:cmd]
when LC_SEGMENT when LC_SEGMENT
seg = cmd seg = cmd
sections = @sections[seg[:segname]] sections = @sections[seg[:segname]]
seg[:fileoff] = offset seg[:fileoff] = offset
sections.each do |sect| sections.each do |sect|
sect[:offset] = offset sect[:offset] = offset
offset += @section_disk_size[sect[:sectname]] offset += @section_disk_size[sect[:sectname]]
end end
when LC_SYMTAB when LC_SYMTAB
if @reloc_info if @reloc_info
# update text section with relocation info # update text section with relocation info
@ -344,14 +344,14 @@ module Assembler
# No else clause is necessary, the first iteration should have caught them. # No else clause is necessary, the first iteration should have caught them.
end end
end # @load_commands.each end # @load_commands.each
end # def calculate_offsets end # def calculate_offsets
####### #######
private private
####### #######
@ -366,8 +366,8 @@ module Assembler
raise "unsupported MachO file type: #{@header.inspect}" raise "unsupported MachO file type: #{@header.inspect}"
end end
end end
end # class MachOFile end # class MachOFile
end # module Assembler end # module Assembler

View file

@ -1,11 +1,11 @@
require 'asm/macho' require 'asm/macho'
module Assembler module Assembler
class MachOSym class MachOSym
attr_accessor :name, :type, :segnum, :desc, :value attr_accessor :name, :type, :segnum, :desc, :value
def initialize(name, type, segnum, desc, value) def initialize(name, type, segnum, desc, value)
@name = name @name = name
@type = type @type = type
@ -13,17 +13,17 @@ module Assembler
@desc = desc @desc = desc
@value = value @value = value
end end
def to_nlist(strx) def to_nlist(strx)
MachO::Nlist.new(strx, @type, @segnum, @desc, @value) MachO::Nlist.new(strx, @type, @segnum, @desc, @value)
end end
def to_s def to_s
@name @name
end end
end end
end end

View file

@ -3,9 +3,9 @@ require 'asm/machosym'
require 'asm/symtab' require 'asm/symtab'
module Assembler module Assembler
class MachOSymtab < Symtab class MachOSymtab < Symtab
include MachO include MachO
def make_symbols(vars, base_addr, type, segnum) def make_symbols(vars, base_addr, type, segnum)
@ -23,7 +23,7 @@ module Assembler
# - All labels are exported. This should be changed and only functions exported! # - All labels are exported. This should be changed and only functions exported!
section = 1 section = 1
# Functions (section #1, __text) # Functions (section #1, __text)
symbols = make_symbols(@labels, text_offset, N_SECT | N_EXT, section) symbols = make_symbols(@labels, text_offset, N_SECT | N_EXT, section)
section += 1 section += 1
@ -46,7 +46,7 @@ module Assembler
def bss_section def bss_section
@consts.size > 0 ? 3 : 2 @consts.size > 0 ? 3 : 2
end end
def nlist_ary def nlist_ary
symbols = {} symbols = {}
strx = 1 strx = 1
@ -61,7 +61,7 @@ module Assembler
end end
return ary return ary
end end
def stab def stab
# The empty strings result in a string that begins and ends with a null byte # The empty strings result in a string that begins and ends with a null byte
['', all_symbols, ''].flatten.map { |sym| sym.to_s }.join("\0") ['', all_symbols, ''].flatten.map { |sym| sym.to_s }.join("\0")
@ -84,5 +84,5 @@ module Assembler
end end
end end
end end

View file

@ -1,4 +1,4 @@
### XXX development hack! ### XXX development hack!
def stub_symtab! def stub_symtab!
text_segnum = 1 text_segnum = 1
@ -8,11 +8,11 @@
['_main', N_SECT | N_EXT, text_segunm, 0x0] ['_main', N_SECT | N_EXT, text_segunm, 0x0]
] ]
} }
nlist_ary = [] nlist_ary = []
stab = "\0" stab = "\0"
strx = 1 # string index (1-based) strx = 1 # string index (1-based)
symtab[:functions].each do |name, type, segnum, addr| symtab[:functions].each do |name, type, segnum, addr|
nlist_ary << MachO::Nlist.new(strx, type, segnum, 0, addr) nlist_ary << MachO::Nlist.new(strx, type, segnum, 0, addr)
stab << "#{name}\0" stab << "#{name}\0"
@ -20,7 +20,7 @@
end end
symtab(nlist_ary, stab) symtab(nlist_ary, stab)
end end
end end
end end

View file

@ -1,11 +1,11 @@
module Assembler module Assembler
class UnimplementedMethodError < RuntimeError; end class UnimplementedMethodError < RuntimeError; end
# Abstract base class. # Abstract base class.
class ObjWriter class ObjWriter
def write!(filename) def write!(filename)
File.open(filename, 'wb') do |file| File.open(filename, 'wb') do |file|
file.print(serialize) file.print(serialize)
@ -15,12 +15,12 @@ module Assembler
def fail(name) def fail(name)
raise UnimplementedMethodError, name raise UnimplementedMethodError, name
end end
# These methods must be defined for most uses of the library. # These methods must be defined for most uses of the library.
%w[header segment section text data bss symtab serialize].each do |name| %w[header segment section text data bss symtab serialize].each do |name|
define_method(name) { fail(name) } define_method(name) { fail(name) }
end end
end end
end end

View file

@ -1,9 +1,9 @@
require 'asm/regproxy' require 'asm/regproxy'
module Assembler module Assembler
module Registers module Registers
# This structure allows for x86 registers of all sizes. The # This structure allows for x86 registers of all sizes. The
# number of the register is the index of the array in which it was # number of the register is the index of the array in which it was
# found. The size of a register in bytes is 2 ** index-into-sub-array. # found. The size of a register in bytes is 2 ** index-into-sub-array.
@ -25,8 +25,8 @@ module Assembler
const_set(name, RegisterProxy.new(reg, 8 * (2 ** i), regnum)) const_set(name, RegisterProxy.new(reg, 8 * (2 ** i), regnum))
end end
end end
end end
end end

View file

@ -1,5 +1,5 @@
module Assembler module Assembler
# Acts like a register and can be used as the base or index in an # Acts like a register and can be used as the base or index in an
# effective address. # effective address.
# #
@ -9,7 +9,7 @@ module Assembler
attr_reader :name, :size, :regnum attr_reader :name, :size, :regnum
attr_reader :base, :index, :scale attr_reader :base, :index, :scale
def initialize(name, size, regnum) def initialize(name, size, regnum)
@name = name # attrs are read-only so sharing is ok @name = name # attrs are read-only so sharing is ok
@size = size @size = size
@ -17,7 +17,7 @@ module Assembler
@base = self @base = self
end end
def +(index) def +(index)
raise "index already specified" if @index raise "index already specified" if @index
new_reg = self.clone new_reg = self.clone
@ -25,7 +25,7 @@ module Assembler
new_reg new_reg
end end
def *(scale) def *(scale)
raise "index must come first" unless @index raise "index must come first" unless @index
raise "scale already specified" if scale raise "scale already specified" if scale
@ -39,7 +39,7 @@ module Assembler
@scale @scale
end end
def index? def index?
@index @index
end end
@ -50,18 +50,18 @@ module Assembler
end end
def to_s def to_s
@name.to_s + @name.to_s +
(@index ? "+#{@index}" : '') + (@index ? "+#{@index}" : '') +
(@scale ? "*#{@scale}" : '') (@scale ? "*#{@scale}" : '')
end end
def inspect def inspect
to_s to_s
end end
end end
end end

View file

@ -9,15 +9,15 @@ module Assembler
# things will actually live in memory. # things will actually live in memory.
class Symtab class Symtab
attr_accessor :text_offset, :bss_offset, :const_offset attr_accessor :text_offset, :bss_offset, :const_offset
attr_reader :const_data, :const_size, :bss_size, :reloc_info attr_reader :const_data, :const_size, :bss_size, :reloc_info
def initialize def initialize
@vars = {} # Map of variable names to offsets. (bss vars) @vars = {} # Map of variable names to offsets. (bss vars)
@consts = {} # Map of constant names to offsets. @consts = {} # Map of constant names to offsets.
@funcs = {} # map of function names to offsets. @funcs = {} # map of function names to offsets.
# Initial data to load into memory (data for __DATA segment). # Initial data to load into memory (data for __DATA segment).
@const_data = '' @const_data = ''
@ -36,7 +36,7 @@ module Assembler
@bss_offset = 0 @bss_offset = 0
@const_offset = 0 @const_offset = 0
end end
# Generate a unique label. # Generate a unique label.
def unique_label(suffix=nil) def unique_label(suffix=nil)
@num_labels += 1 @num_labels += 1
@ -52,19 +52,19 @@ module Assembler
@labels[name] = offset @labels[name] = offset
return name return name
end end
def lookup_label(name) def lookup_label(name)
@labels[name] @labels[name]
end end
def defvar(name, bytes) def defvar(name, bytes)
@vars[name] = @bss_size @vars[name] = @bss_size
@bss_size += bytes @bss_size += bytes
end end
def defconst(name, value, bytes) def defconst(name, value, bytes)
@consts[name] = @const_size @consts[name] = @const_size
@const_size += bytes @const_size += bytes
@ -76,15 +76,15 @@ module Assembler
@funcs[name] = offset @funcs[name] = offset
end end
def var(name) def var(name)
@vars[name] @vars[name]
end end
def var?(name) def var?(name)
@vars.has_key?(name) @vars.has_key?(name)
end end
def const(name) def const(name)
@consts[name] @consts[name]
end end
@ -94,5 +94,5 @@ module Assembler
end end
end end
end end

View file

@ -1,5 +1,5 @@
# A subset of x86 assembly. # A subset of x86 assembly.
# #
# sjs # sjs
# may 2009 # may 2009
@ -174,7 +174,7 @@ module Assembler
def int(num) def int(num)
emit("int 0x#{num.to_s(16)}") emit("int 0x#{num.to_s(16)}")
end end
def cdq def cdq
emit("cdq") emit("cdq")
end end

View file

@ -1,5 +1,5 @@
module Assembler module Assembler
# Wrap a variable's address so that we can perform arithmetic on it # Wrap a variable's address so that we can perform arithmetic on it
# before resolving it when we know where things will go in memory. # before resolving it when we know where things will go in memory.
# All we do is catch arithmetic ops and then provide a means to # All we do is catch arithmetic ops and then provide a means to
@ -10,7 +10,7 @@ module Assembler
attr_reader :name attr_reader :name
attr_accessor :ops attr_accessor :ops
def initialize(name, const=false) def initialize(name, const=false)
@name = name @name = name
@const = const @const = const
@ -31,7 +31,7 @@ module Assembler
addr.send(*op) addr.send(*op)
end end
end end
def const? def const?
@const @const
end end

View file

@ -18,7 +18,7 @@ class ParseError < StandardError
end end
class Compiler class Compiler
include Assembler::Registers include Assembler::Registers
Keywords = { Keywords = {
@ -280,7 +280,7 @@ class Compiler
false_label = asm.mklabel(:false) false_label = asm.mklabel(:false)
truthy_label = asm.mklabel(:truthy) truthy_label = asm.mklabel(:truthy)
done_label = asm.mklabel(:done) done_label = asm.mklabel(:done)
asm.cmp(EAX, FALSE) asm.cmp(EAX, FALSE)
asm.jne(truthy_label) asm.jne(truthy_label)
@ -306,7 +306,7 @@ class Compiler
expected('&&') unless match_word('&&') expected('&&') unless match_word('&&')
false_label = asm.mklabel(:false) false_label = asm.mklabel(:false)
done_label = asm.mklabel(:done) done_label = asm.mklabel(:done)
asm.cmp(EAX, FALSE) asm.cmp(EAX, FALSE)
asm.je(false_label) asm.je(false_label)
@ -382,7 +382,7 @@ class Compiler
# a: <on the stack> # a: <on the stack>
# b: eax # b: eax
# #
# If b - a is zero then a = b, and make_boolean will leave the zero # If b - a is zero then a = b, and make_boolean will leave the zero
# to effectively return false. If b - a is non-zero then a != b, # to effectively return false. If b - a is non-zero then a != b,
# and make_boolean will leave -1 (true) for us in eax. # and make_boolean will leave -1 (true) for us in eax.
@ -409,8 +409,8 @@ class Compiler
# the assembler needed to implement, but since the Jcc # the assembler needed to implement, but since the Jcc
# instructions are very cheap to implement this is no longer # instructions are very cheap to implement this is no longer
# a concern. # a concern.
# The next 4 relations all compare 2 values a and b, then return # The next 4 relations all compare 2 values a and b, then return
# true (-1) if the difference was below zero and false (0) # true (-1) if the difference was below zero and false (0)
# otherwise (using JL, jump if less than). # otherwise (using JL, jump if less than).
@ -464,7 +464,7 @@ class Compiler
# a: <on the stack> # a: <on the stack>
# b: eax # b: eax
# #
# if a <= b then !(a > b) # if a <= b then !(a > b)
def le_relation def le_relation
# Compare them as in greater than but invert the result. # Compare them as in greater than but invert the result.
@ -506,7 +506,7 @@ class Compiler
end end
@indent -= 1 @indent -= 1
end end
# Parse an if-else statement. # Parse an if-else statement.
def if_else_stmt def if_else_stmt
else_label = asm.mklabel(:end_or_else) else_label = asm.mklabel(:end_or_else)
@ -528,7 +528,7 @@ class Compiler
end end
# Used to implement the Two-Label-Loops (while, until, repeat). # Used to implement the Two-Label-Loops (while, until, repeat).
# #
# name: Name of the loop for readable labels. # name: Name of the loop for readable labels.
# block: Code to execute at the start of each iteration. (e.g. a # block: Code to execute at the start of each iteration. (e.g. a
# condition) # condition)
@ -667,7 +667,7 @@ class Compiler
# 12 bytes: 2 for "0x", 8 hex digits, 2 for newline + null terminator # 12 bytes: 2 for "0x", 8 hex digits, 2 for newline + null terminator
hex = asm.var!(h, 12) hex = asm.var!(h, 12)
asm.block do asm.block do
# TODO check sign and prepend '-' if negative # TODO check sign and prepend '-' if negative
mov([hex], 0x7830) # "0x" ==> 48, 120 mov([hex], 0x7830) # "0x" ==> 48, 120
@ -757,10 +757,10 @@ class Compiler
@look = if @input.eof? @look = if @input.eof?
nil nil
else else
@input.readbyte.chr @input.readbyte.chr
end end
end end
# Report error and halt # Report error and halt
def abort(msg) def abort(msg)
raise ParseError, msg raise ParseError, msg
@ -777,7 +777,7 @@ class Compiler
raise ParseError.new(caller, context), "Expected #{what} but got #{got}." raise ParseError.new(caller, context), "Expected #{what} but got #{got}."
end end
end end
# Recognize an alphabetical character. # Recognize an alphabetical character.