diff --git a/asm/binary.rb b/asm/binary.rb index 945f95c..bb048ef 100644 --- a/asm/binary.rb +++ b/asm/binary.rb @@ -616,7 +616,7 @@ def inc(op) def dec(op) if register?(op) - # dec r16 / dec r32 + # dec reg32 asm { emit_byte(0x48 + op.regnum) } else raise "unsupported DEC instruction, op=#{op.inspect}" @@ -655,6 +655,7 @@ def and_(dest, src) raise "unsupported AND instruction: dest=#{dest.inspect}, src=#{src.inspect}" end end + alias_method :and, :and_ def xor(dest, src) @@ -674,6 +675,7 @@ def xor(dest, src) def not_(op) group3(op, 2, 'NOT') end + alias_method :not, :not_ def neg(op) @@ -816,6 +818,7 @@ def loop_(label) emit_byte(delta) end end + alias_method :loop, :loop_ # Opcode group #3. 1-byte opcode, 1 operand (r/m8 or r/m32). diff --git a/asm/cstruct.rb b/asm/cstruct.rb index 574505d..7ca5c8b 100644 --- a/asm/cstruct.rb +++ b/asm/cstruct.rb @@ -304,10 +304,9 @@ class MachHeader < CStruct puts MachHeader::MemberSizes.inspect puts "# of MachHeader members: " + MachHeader.size.to_s + ", size in bytes: " + MachHeader.bytesize.to_s mh = MachHeader.new(0xfeedface, 7, 3, "foobar") - puts "magic(#{MachHeader.sizeof(:magic)}): " + mh[:magic].inspect - puts "cputype(#{MachHeader.sizeof(:cputype)}): " + mh[:cputype].inspect - puts "cpusubtype(#{MachHeader.sizeof(:cpusubtype)}): " + mh[:cpusubtype].inspect - puts "segname(#{MachHeader.sizeof(:segname)}): " + mh[:segname].inspect + %w[magic, cputype, cpusubtype, segname].each do |field| + puts "#{field}(#{MachHeader.sizeof(field.to_sym)}): " + mh[field.to_sym].inspect + end puts mh.pack_pattern.inspect binstr = mh.serialize puts "values: " + mh.values.inspect @@ -317,4 +316,4 @@ class MachHeader < CStruct puts "serialized: " + binstr.inspect puts "unserialized: " + newbinstr.inspect puts "new == old ? " + (newbinstr == binstr).to_s -end \ No newline at end of file +end diff --git a/asm/machofile.rb b/asm/machofile.rb index 273cfb1..6684707 100644 --- a/asm/machofile.rb +++ b/asm/machofile.rb @@ -131,56 +131,46 @@ def text(data, sectname='__text', segname='__TEXT') end - # Define a standard data section under the current segment (if present). - # This behaves similarly to the text method. - # - def data(data, sectname='__data', segname='__DATA') + # Basis for #data, #const, and #bss methods. + def segment_based_on_filetype(segname, options={}) unless @current_segment + permissions = VM_PROT_READ + permisions |= VM_PROT_WRITE if options.delete(:writable) segment(segname_based_on_filetype(segname)) do |seg| - seg[:maxprot] = VM_PROT_READ | VM_PROT_WRITE - seg[:initprot] = VM_PROT_READ | VM_PROT_WRITE + seg[:initprot] = seg[:maxprot] = permissions end end - - section(sectname, segname, data) - + yield if block_given? return self end + # Define a standard data section under the current segment (if present). + # This behaves similarly to the text method. + # + def data(data, sectname='__data', segname='__DATA') + segment_based_on_filetype(segname, :writable => true) do + section(sectname, segname, data) + end + end # Define a standard const section under the current segment (if present). # This behaves similarly to the data method. # def const(data, sectname='__const', segname='__DATA') - unless @current_segment - segment(segname_based_on_filetype(segname)) do |seg| - seg[:maxprot] = VM_PROT_READ - seg[:initprot] = VM_PROT_READ - end + segment_based_on_filetype(segname) do + section(sectname, segname, data) end - - section(sectname, segname, data) - - return self end - # Define a standard BSS section under the current segment (if present). # This behaves similarly to the data method but accepts a VM size instead # of a blob, and no data is written to file since this section is for # uninitialized data. # def bss(vmsize, sectname='__bss', segname='__DATA') - unless @current_segment - segment(segname_based_on_filetype(segname)) do |seg| - seg[:maxprot] = VM_PROT_READ | VM_PROT_WRITE - seg[:initprot] = VM_PROT_READ | VM_PROT_WRITE - end + segment_based_on_filetype(segname, :writable => true) do + section(sectname, segname, '', vmsize) end - - section(sectname, segname, '', vmsize) - - return self end diff --git a/asm/machosymtab.rb b/asm/machosymtab.rb index c6db0aa..1cb8263 100644 --- a/asm/machosymtab.rb +++ b/asm/machosymtab.rb @@ -18,37 +18,22 @@ def bss_offset return 0x2800 end - def all_symbols - symbols = [] - - # Functions (section #1, __text) - # - # All labels are exported. This should be changed and only functions exported! - # TODO fixme ... - # + def make_symbols(vars, type, segnum) # Note: Sorting a Ruby hash gives an alist, e.g. [[, ], ...] # We can use map on it as if it were a hash so it works nicely. - # - symbols += - @labels.sort { |a,b| a[1] <=> b[1] }. - map do |name,addr| - MachOSym.new(name, N_SECT | N_EXT, 1, 0, addr) - end - - # Constants (section #2, __const) - symbols += @consts.sort { |a,b| a[1] <=> b[1] }. - map do |name, addr| - MachOSym.new(name, N_SECT, 2, 0, addr) - end - - # Variables (section #3, __bss) - # - # TODO FIXME the last var exported ends up after main somewhere... WTF?! - symbols += @vars.sort { |a,b| a[1] <=> b[1] }. - map do |name, addr| - MachOSym.new(name, N_SECT, 3, 0, addr) - end - + vars.sort { |a,b| a[1] <=> b[1] }. + map do |name, addr| + MachOSym.new(name, type, segnum, 0, addr) + end + end + + def all_symbols + # TODO FIXME: + # - the last var exported ends up after main somewhere... WTF?! + # - All labels are exported. This should be changed and only functions exported! + symbols = make_symbols(@labels, N_SECT | N_EXT, 1) + # Functions (section #1, __text) + make_symbols(@consts, N_SECT, 2) + # Constants (section #2, __const) + make_symbols(@vars, N_SECT, 3) # Variables (section #3, __bss) return symbols end diff --git a/build.rb b/build.rb index 323114c..19fa2db 100755 --- a/build.rb +++ b/build.rb @@ -49,16 +49,21 @@ def compile(infile, outfile, asm) exit(1) end -# assemble using nasm, return resulting filename. -def assemble(filename, binformat='elf') - f = base(filename) - outfile = "#{f}.o" - output = `nasm -f #{binformat} -g -o #{outfile} #{filename} 2>&1` +def run_and_warn_on_failure(command, name) + output = `#{command}` if $?.exitstatus != 0 puts print output - raise "nasm failed: #{$?.exitstatus}" + name = command.split.first + raise "#{name} failed: #{$?.exitstatus}" end +end + +# assemble using nasm, return resulting filename. +def assemble(filename, binformat='elf') + f = base(filename) + outfile = "#{f}.o" + run_and_warn_on_failure("nasm -f #{binformat} -g -o #{outfile} #{filename} 2>&1") return outfile end @@ -71,12 +76,7 @@ def link(filename, platform='linux') else raise "unsupported platform: #{platform}" end - output = `#{cmd} #{args} -o #{f} #{filename} 2>&1` - if $?.exitstatus != 0 - puts - print output - raise "ld failed: #{$?.exitstatus}" - end + run_and_warn_on_failure("#{cmd} #{args} -o #{f} #{filename} 2>&1") `chmod u+x #{f}` return f end diff --git a/compiler.rb b/compiler.rb index 0b372cd..02fddda 100644 --- a/compiler.rb +++ b/compiler.rb @@ -190,25 +190,23 @@ def divide # bit expressions # ################### - def bitor_expr - match('|') - term + def bit_expr(op, token) + match(token) + if block_given? yield else term end asm.pop(EBX) - asm.or_(EAX, EBX) + asm.send(op, EAX, EBX) end - def bitand_expr - match('&') - signed_factor - asm.pop(EBX) - asm.and_(EAX, EBX) + def bitor_expr + bit_expr(:or, '|') end def xor_expr - match('^') - term - asm.pop(EBX) - asm.xor(EAX, EBX) + bit_expr(:xor, '^') + end + + def bitand_expr + bit_expr(:and, '&') { signed_factor } end @@ -323,6 +321,9 @@ def eq_relation # true (-1) if the difference was below zero and false (0) # otherwise (using JL, jump if less than). def cmp_relation(a, b, options={}) + expression + asm.pop(EBX) + # Invert the sense of the test? invert = options[:invert] @@ -347,8 +348,6 @@ def cmp_relation(a, b, options={}) # # if a > b then b - a < 0 def gt_relation - expression - asm.pop(EBX) cmp_relation(EAX, EBX) # b - a end @@ -357,8 +356,6 @@ def gt_relation # # if a < b then a - b < 0 def lt_relation - expression - asm.pop(EBX) cmp_relation(EBX, EAX) # a - b end @@ -367,8 +364,6 @@ def lt_relation # # if a >= b then !(a < b) def ge_relation - expression - asm.pop(EBX) # Compare them as in less than but invert the result. cmp_relation(EBX, EAX, :invert => true) end @@ -378,8 +373,6 @@ def ge_relation # # if a <= b then !(a > b) def le_relation - expression - asm.pop(EBX) # Compare them as in greater than but invert the result. cmp_relation(EAX, EBX, :invert => true) end @@ -474,20 +467,20 @@ def simple_loop(name) asm.emit_label(end_label) end - def while_stmt - simple_loop('while') do |end_label| + def condition_loop(name, jump_instruction) + simple_loop(name) do |end_label| condition skip_any_whitespace - asm.jz(end_label) + asm.send(jump_instruction, end_label) end end + def while_stmt + condition_loop('while', :jz) # done when == 0 (falsish) + end + def until_stmt - simple_loop('until') do |end_label| - condition - skip_any_whitespace - asm.jnz(end_label) - end + condition_loop('until', :jnz) # done when != 0 (truthy) end def repeat_stmt