mirror of
https://github.com/samsonjs/compiler.git
synced 2026-04-27 14:57:45 +00:00
remove trailing whitespace
This commit is contained in:
parent
87672a1a00
commit
8013b8796c
17 changed files with 243 additions and 243 deletions
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
100
asm/binary.rb
100
asm/binary.rb
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
module Assembler
|
module Assembler
|
||||||
|
|
||||||
class ELFSymtab < Symtab
|
class ELFSymtab < Symtab
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
module Assembler
|
module Assembler
|
||||||
|
|
||||||
class ELFWriter < ObjWriter
|
class ELFWriter < ObjWriter
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
44
asm/macho.rb
44
asm/macho.rb
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
26
compiler.rb
26
compiler.rb
|
|
@ -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.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue