changeset 336:d1ecc493384e

Added spiffy armtoken class for bit fiddeling. Added cool test that checks for build repeatability
author Windel Bouwman
date Wed, 19 Feb 2014 22:32:15 +0100
parents 582a1aaa3983
children b00219172a42
files examples/c3/recipe.yaml examples/c3/startup_stm32f4.asm kernel/recipe.yaml python/ppci/__init__.py python/ppci/assembler.py python/ppci/buildtasks.py python/ppci/c3/astnodes.py python/ppci/c3/codegenerator.py python/ppci/c3/parser.py python/ppci/codegen/codegen.py python/ppci/ir.py python/ppci/linker.py python/ppci/objectfile.py python/ppci/transform.py python/target/arm.brg python/target/arminstructions.py python/target/arminstructionselector.py python/target/instructionselector.py python/zcc.py test/testbintools.py test/testzcc.py user/recipe.yaml
diffstat 22 files changed, 338 insertions(+), 111 deletions(-) [+]
line wrap: on
line diff
--- a/examples/c3/recipe.yaml	Mon Feb 17 20:41:30 2014 +0100
+++ b/examples/c3/recipe.yaml	Wed Feb 19 22:32:15 2014 +0100
@@ -9,6 +9,8 @@
        includes: [stm32f4xx.c3]
        machine: arm
        output: burn.elf2
-  code: 0x08000000
-  data: 0x20000000
+  output: burn2.bin
+  layout:
+    code: 0x08000000
+    data: 0x20000000
 
--- a/examples/c3/startup_stm32f4.asm	Mon Feb 17 20:41:30 2014 +0100
+++ b/examples/c3/startup_stm32f4.asm	Wed Feb 19 22:32:15 2014 +0100
@@ -2,5 +2,5 @@
 
 DCD 0x20000678  ; Setup stack pointer
 DCD 0x08000009  ; Reset vector, jump to address 8
-B main          ; Branch to main (this is actually in the interrupt vector)
+B burn2_main          ; Branch to main (this is actually in the interrupt vector)
 
--- a/kernel/recipe.yaml	Mon Feb 17 20:41:30 2014 +0100
+++ b/kernel/recipe.yaml	Wed Feb 19 22:32:15 2014 +0100
@@ -2,13 +2,15 @@
 link:
   inputs:
     - assemble:
-       source: ../examples/c3/startup_stm32f4.asm
+       source: startup_m3.asm
        machine: arm
     - compile:
        sources: [memory.c3, kernel.c3, syscall.c3, process.c3, schedule.c3, arch_arm.c3]
        includes: []
        machine: arm
        output: kernel.elf2
-  code: 0x8000
-  data: 0x9000
+  layout:
+     code: 0x0
+     data: 0x20000000
+  output: kernel.bin
 
--- a/python/ppci/__init__.py	Mon Feb 17 20:41:30 2014 +0100
+++ b/python/ppci/__init__.py	Wed Feb 19 22:32:15 2014 +0100
@@ -1,6 +1,7 @@
 # File to make this directory a package.
 
 import sys
+import os
 
 version = '0.0.1'
 
@@ -14,3 +15,5 @@
 
 logformat='%(asctime)s|%(levelname)s|%(name)s|%(message)s'
 
+def same_dir(full_path, filename):
+    return os.path.join(os.path.dirname(os.path.abspath(full_path)), filename)
--- a/python/ppci/assembler.py	Mon Feb 17 20:41:30 2014 +0100
+++ b/python/ppci/assembler.py	Wed Feb 19 22:32:15 2014 +0100
@@ -151,7 +151,7 @@
 asmParser = Parser()
 
 class Assembler:
-    def __init__(self, target=None, stream=None):
+    def __init__(self, target, stream):
         self.target = target
         self.stream = stream
         self.restart()
--- a/python/ppci/buildtasks.py	Mon Feb 17 20:41:30 2014 +0100
+++ b/python/ppci/buildtasks.py	Wed Feb 19 22:32:15 2014 +0100
@@ -31,10 +31,12 @@
     def __init__(self, source, target, output_object):
         super().__init__('Assemble')
         self.source = source
-        self.assembler = Assembler(target=target)
         self.output = output_object
+        self.ostream = outstream.BinaryOutputStream(self.output)
+        self.assembler = Assembler(target, self.ostream)
 
     def run(self):
+        self.ostream.selectSection('code')
         self.assembler.assemble(self.source)
 
 
@@ -82,16 +84,22 @@
 
 class Link(BuildTask):
     """ Link together a collection of object files """
-    def __init__(self, objects, output_file):
+    def __init__(self, objects, layout, output_file):
         super().__init__('Link')
         self.objects = objects
         self.linker = Linker()
         self.duration = 0.1337
+        self.layout = layout
+        self.output_file = output_file
 
     def run(self):
-        self.linker.link(self.objects)
+        output_obj = self.linker.link(self.objects, self.layout)
+        code = output_obj.get_section('code').data
+        with open(self.output_file, 'wb') as f:
+            f.write(code)
 
 
-class ObjCopy(Task):
-    pass
+class ObjCopy(BuildTask):
+    def __init__(self, objects, output_file):
+        super().__init__('ObjCopy')
 
--- a/python/ppci/c3/astnodes.py	Mon Feb 17 20:41:30 2014 +0100
+++ b/python/ppci/c3/astnodes.py	Wed Feb 19 22:32:15 2014 +0100
@@ -37,6 +37,11 @@
         self.declarations = []
         self.imports = []
 
+    def add_declaration(self, decl):
+        self.declarations.append(decl)
+        if isinstance(decl, Function):
+            decl.package = self
+
     def __repr__(self):
         return 'MODULE {}'.format(self.name)
 
@@ -174,6 +179,9 @@
         self.loc = loc
         self.declarations = []
 
+    def add_declaration(self, decl):
+        self.declarations.append(decl)
+
     def __repr__(self):
         return 'Func {}'.format(self.name)
 
--- a/python/ppci/c3/codegenerator.py	Mon Feb 17 20:41:30 2014 +0100
+++ b/python/ppci/c3/codegenerator.py	Wed Feb 19 22:32:15 2014 +0100
@@ -312,7 +312,7 @@
         if type(tg) is not ast.Function:
             raise SemanticError('cannot call {}'.format(tg))
         ftyp = tg.typ
-        fname = tg.name
+        fname = tg.package.name + '_' + tg.name
         ptypes = ftyp.parametertypes
         if len(expr.args) != len(ptypes):
             raise SemanticError('{} requires {} arguments, {} given'
--- a/python/ppci/c3/parser.py	Mon Feb 17 20:41:30 2014 +0100
+++ b/python/ppci/c3/parser.py	Wed Feb 19 22:32:15 2014 +0100
@@ -60,7 +60,7 @@
         return t
 
     def addDeclaration(self, decl):
-        self.currentPart.declarations.append(decl)
+        self.currentPart.add_declaration(decl)
 
     def parseImport(self):
         self.Consume('import')
--- a/python/ppci/codegen/codegen.py	Mon Feb 17 20:41:30 2014 +0100
+++ b/python/ppci/codegen/codegen.py	Wed Feb 19 22:32:15 2014 +0100
@@ -1,4 +1,4 @@
-from ..ir import Module
+from .. import ir
 from ..irutils import Verifier
 from target import Target
 from ppci import CompilerError
@@ -23,7 +23,7 @@
         """ Generate code for one function into a frame """
         self.logger.debug('Generating code for {}'.format(irfunc.name))
         # Create a frame for this function:
-        frame = self.target.FrameClass(irfunc.name)
+        frame = self.target.FrameClass(ir.label_name(irfunc))
 
         # Canonicalize the intermediate language:
         canonicalize(irfunc, frame)
@@ -48,7 +48,7 @@
 
     def generate(self, ircode, outs):
         """ Generate code into output stream """
-        assert isinstance(ircode, Module)
+        assert isinstance(ircode, ir.Module)
         outs.selectSection('code')
 
         # Munch program into a bunch of frames. One frame per function.
--- a/python/ppci/ir.py	Mon Feb 17 20:41:30 2014 +0100
+++ b/python/ppci/ir.py	Wed Feb 19 22:32:15 2014 +0100
@@ -3,6 +3,19 @@
 """
 
 
+def label_name(dut):
+    """ Function that returns the assembly code label name """
+    if isinstance(dut, Block):
+        f = dut.function
+        return label_name(f) + '_' + dut.name
+    elif isinstance(dut, Function):
+        return label_name(dut.module) + '_' + dut.name
+    elif isinstance(dut, Module):
+        return dut.name
+    else:
+        raise NotImplementedError(str(dut))
+
+
 class Module:
     """ Container unit for variables and functions. """
     def __init__(self, name):
@@ -16,8 +29,9 @@
     def add_function(self, f):
         """ Add a function to this module """
         self.functions.append(f)
+        f.module = self
 
-    def addVariable(self, v):
+    def add_variable(self, v):
         self.variables.append(v)
 
     def getVariables(self):
@@ -43,9 +57,9 @@
     """ Represents a function. """
     def __init__(self, name, module=None):
         self.name = name
-        self.entry = Block('{}_entry'.format(name))
+        self.entry = Block('entry')
         self.entry.function = self
-        self.epiloog = Block('{}_epilog'.format(name))
+        self.epiloog = Block('epilog')
         self.epiloog.function = self
         self.epiloog.addInstruction(Terminator())
         self.return_value = Temp('{}_retval'.format(name))
@@ -195,6 +209,7 @@
 class Call(Expression):
     """ Call a function with some arguments """
     def __init__(self, f, arguments):
+        assert type(f) is str
         self.f = f
         self.arguments = arguments
 
--- a/python/ppci/linker.py	Mon Feb 17 20:41:30 2014 +0100
+++ b/python/ppci/linker.py	Wed Feb 19 22:32:15 2014 +0100
@@ -22,19 +22,71 @@
 
 
 @reloc('lit_add_8')
-class LitAdd8:
-    def apply(self):
-        pass
+def apply_lit8(reloc, sym, section, reloc_value):
+    assert sym.value % 4 == 0
+    offset = (sym.value - (align(reloc_value + 2, 4)))
+    assert offset in range(0, 1024, 4), str(offset)+str( self.dst.sections)
+    rel8 = offset >> 2
+    section.data[reloc.offset] = rel8
+
+
+@reloc('wrap_new11')
+def apply_wrap_new11(reloc, sym, section, reloc_value):
+    offset = sym.value - (align(reloc_value, 2) + 4)
+    assert offset in range(-2048, 2046, 2)
+    imm11 = wrap_negative(offset >> 1, 11)
+    section.data[reloc.offset] = (imm11 & 0xff)
+    section.data[reloc.offset + 1] |= (imm11 >> 8) & 0x7
+
+
+@reloc('rel8')
+def apply_rel8(reloc, sym, section, reloc_value):
+    assert sym.value % 2 == 0
+    offset = sym.value - (align(reloc_value, 2) + 4)
+    assert offset in range(-256, 254, 2), str(offset) + str(reloc)
+    imm8 = wrap_negative(offset >> 1, 8)
+    section.data[reloc.offset] = imm8
+
+
+@reloc('bl_imm11_imm10')
+def apply_bl_imm11(reloc, sym, section, reloc_value):
+    assert sym.value % 2 == 0
+    offset = sym.value - (align(reloc_value, 2) + 4)
+    assert offset in range(-16777216, 16777214, 2), str(offset)
+    imm32 = wrap_negative(offset >> 1, 32)
+    imm11 = imm32 & 0x7FF
+    imm10 = (imm32 >> 11) & 0x3FF
+    s = (imm32 >> 24) & 0x1
+    section.data[reloc.offset + 2] = imm11 & 0xFF
+    section.data[reloc.offset + 3] |= (imm11 >> 8) & 0x7
+    section.data[reloc.offset] = imm10 & 0xff
+    section.data[reloc.offset + 1] |= ((imm10 >> 8) & 0x3) | (s << 2)
+
+@reloc('b_imm11_imm6')
+def apply_b_imm11_imm6(reloc, sym, section, reloc_value):
+    assert sym.value % 2 == 0
+    offset = sym.value - (align(reloc_value, 2) + 4)
+    assert offset in range(-1048576, 1048574, 2), str(offset)
+    imm32 = wrap_negative(offset >> 1, 32)
+    imm11 = imm32 & 0x7FF
+    imm6 = (imm32 >> 11) & 0x3F
+    s = (imm32 >> 24) & 0x1
+    section.data[reloc.offset + 2] = imm11 & 0xFF
+    section.data[reloc.offset + 3] |= (imm11 >> 8) & 0x7
+    section.data[reloc.offset] |= imm6
+    section.data[reloc.offset + 1] |= (s << 2)
 
 
 class Linker:
-    def set_symbol(self, sym):
-        self.dst.add_symbol(sym.name, sym.value, sym.section)
-
-    def link(self, objs):
+    """ Merges the sections of several object files and 
+        performs relocation """
+    def link(self, objs, layout={}):
         # Create new object file to store output:
         self.dst = ObjectFile()
-        self.dst.get_section('code').address = 0x08000000
+
+        # Create sections with address:
+        for section_name, address in layout.items():
+            self.dst.get_section(section_name).address = address
 
         # First copy all sections into output sections:
         for iobj in objs:
@@ -42,19 +94,26 @@
             # Merge sections:
             for in_s in iobj.sections.values():
                 out_s = self.dst.get_section(in_s.name)
-                offsets[in_s.name] = out_s.address + out_s.Size
+                # TODO: align section in other way:
+                while out_s.Size % 4 != 0:
+                    out_s.add_data(bytes([0]))
+
+                # Add new section:
+                offsets[in_s.name] = out_s.Size
                 out_s.add_data(in_s.data)
-                # TODO: align section
+
 
             # Merge symbols:
             for sym in iobj.symbols.values():
-                self.set_symbol(sym)
+                out_s = self.dst.get_section(sym.section)
+                value = offsets[sym.section] + out_s.address + sym.value
+                self.dst.add_symbol(sym.name, value, sym.section)
 
             # Merge relocations:
             for reloc in iobj.relocations:
-                self.dst.add_relocation(reloc.sym, reloc.offset, reloc.typ, reloc.section)
+                offset = offsets[reloc.section] + reloc.offset
+                self.dst.add_relocation(reloc.sym, offset, reloc.typ, reloc.section)
 
-        # Check that there are no more unresolved symbols:
         # Perform relocations:
         for reloc in self.dst.relocations:
             # Lookup symbol:
@@ -64,48 +123,12 @@
             # patch up:
             section = self.dst.get_section(reloc.section)
 
-            if reloc.typ == 'lit_add_8':
-                assert sym.value % 4 == 0
-                offset = (sym.value - (align(reloc.offset + 2, 4)))
-                assert offset in range(0, 1024, 4)
-                rel8 = offset >> 2
-                section.data[reloc.offset] = rel8
-            elif reloc.typ == 'wrap_new11':
-                offset = sym.value - (align(reloc.offset, 2) + 4)
-                assert offset in range(-2048, 2046, 2)
-                imm11 = wrap_negative(offset >> 1, 11)
-                section.data[reloc.offset] = (imm11 & 0xff)
-                section.data[reloc.offset + 1] |= (imm11 >> 8) & 0x7
-            elif reloc.typ == 'rel8':
-                assert sym.value % 2 == 0
-                offset = sym.value - (align(reloc.offset, 2) + 4)
-                assert offset in range(-256, 254, 2), str(offset) + str(reloc)
-                imm8 = wrap_negative(offset >> 1, 8)
-                section.data[reloc.offset] = imm8
-            elif reloc.typ == 'bl_imm11_imm10':
-                assert sym.value % 2 == 0
-                offset = sym.value - (align(reloc.offset, 2) + 4)
-                assert offset in range(-16777216, 16777214, 2), str(offset)
-                imm32 = wrap_negative(offset >> 1, 32)
-                imm11 = imm32 & 0x7FF
-                imm10 = (imm32 >> 11) & 0x3FF
-                s = (imm32 >> 24) & 0x1
-                section.data[reloc.offset + 2] = imm11 & 0xFF
-                section.data[reloc.offset + 3] |= (imm11 >> 8) & 0x7
-                section.data[reloc.offset] = imm10 & 0xff
-                section.data[reloc.offset + 1] |= ((imm10 >> 8) & 0x3) | (s << 2)
-            elif reloc.typ == 'b_imm11_imm6':
-                assert sym.value % 2 == 0
-                offset = sym.value - (align(reloc.offset, 2) + 4)
-                assert offset in range(-1048576, 1048574, 2), str(offset)
-                imm32 = wrap_negative(offset >> 1, 32)
-                imm11 = imm32 & 0x7FF
-                imm6 = (imm32 >> 11) & 0x3F
-                s = (imm32 >> 24) & 0x1
-                section.data[reloc.offset + 2] = imm11 & 0xFF
-                section.data[reloc.offset + 3] |= (imm11 >> 8) & 0x7
-                section.data[reloc.offset] |= imm6
-                section.data[reloc.offset + 1] |= (s << 2)
+            # Determine location in memory of reloc patchup position:
+            reloc_value = section.address + reloc.offset
+
+            if reloc.typ in reloc_map:
+                f = reloc_map[reloc.typ]
+                f(reloc, sym, section, reloc_value)
             else:
                 raise NotImplementedError('Unknown relocation type {}'.format(reloc.typ))
 
--- a/python/ppci/objectfile.py	Mon Feb 17 20:41:30 2014 +0100
+++ b/python/ppci/objectfile.py	Wed Feb 19 22:32:15 2014 +0100
@@ -4,6 +4,8 @@
 is code, symbol table and relocation information.
 """
 
+from . import CompilerError
+
 class Symbol:
     def __init__(self, name, value, section):
         self.name = name
@@ -43,17 +45,26 @@
 
 
 class ObjectFile:
+    """ Container for sections with compiled code or data.
+        Also contains symbols and relocation entries """
     def __init__(self):
         self.symbols = {}
         self.sections = {}
         self.relocations = []
 
+    def find_symbol(self, name):
+        return self.symbols[name]
+
     def add_symbol(self, name, value, section):
+        if name in self.symbols:
+            raise CompilerError('{} already defined'.format(name))
+        assert section in self.sections
         sym = Symbol(name, value, section)
         self.symbols[name] = sym
         return sym
 
     def add_relocation(self, sym_name, offset, typ, section):
+        assert section in self.sections
         reloc = Relocation(sym_name, offset, typ, section)
         self.relocations.append(reloc)
         return reloc
--- a/python/ppci/transform.py	Mon Feb 17 20:41:30 2014 +0100
+++ b/python/ppci/transform.py	Wed Feb 19 22:32:15 2014 +0100
@@ -157,7 +157,7 @@
 
     def glue_blocks(self, block1, block2, f):
         """ Glue two blocks together into the first block """
-        self.logger.debug('Glueing {} and {}'.format(block1, block2))
+        self.logger.debug('Merging {} and {}'.format(block1.name, block2.name))
 
         # Remove the last jump:
         block1.removeInstruction(block1.LastInstruction)
--- a/python/target/arm.brg	Mon Feb 17 20:41:30 2014 +0100
+++ b/python/target/arm.brg	Wed Feb 19 22:32:15 2014 +0100
@@ -4,6 +4,7 @@
 from target.arminstructions import B, Bl, Bgt, Blt, Beq, Bne
 from target.arminstructions import Mov2, Mov3
 from target.arminstructions import Add, Sub, Cmp, Sub2, Add2, Mul
+from ppci import ir
 
 %%
 
--- a/python/target/arminstructions.py	Mon Feb 17 20:41:30 2014 +0100
+++ b/python/target/arminstructions.py	Wed Feb 19 22:32:15 2014 +0100
@@ -10,6 +10,55 @@
 def u32(x):
     return struct.pack('<I', x)
 
+
+def val2bit(v, bits):
+    b = []
+    for i in range(bits):
+        b.append(bool((1<<i) & v))
+    #b.reverse()
+    return b
+
+
+def bit_range(b, e):
+    getter = lambda s: s[b:e]
+    def setter(s, v):
+        s[b:e] = v
+    return property(getter, setter)
+
+class ArmToken:
+    def __init__(self):
+        self.bit_value = 0
+
+    def set_bit(self, i, value):
+        value = bool(value)
+        assert i in range(0, 16)
+        mask = 1 << i
+        if value:
+            self.bit_value |= mask
+        else:
+            self.bit_value &= (~mask)
+
+    def __getitem__(self, key):
+        return False
+
+    def __setitem__(self, key, value):
+        if type(key) is int:
+            self.set_bit(key, value)
+        elif type(key) is slice:
+            assert key.step is None
+            bits = key.stop - key.start
+            value_bits = val2bit(value, bits)
+            for i in range(key.start, key.stop):
+                self.set_bit(i, value_bits[i - key.start])
+        else:
+            raise KeyError()
+
+    rd = bit_range(0, 3)
+
+    def encode(self):
+        return u16(self.bit_value)
+
+
 # Operands:
 
 class ArmRegister(Register):
@@ -440,11 +489,16 @@
         return cls(im.dst[0], im.src[0], im.src[1])
 
     def encode(self):
-        rd = self.rd.num
+        at = ArmToken()
+        at.rd = self.rd.num
         rn = self.rn.num
         rm = self.rm.num
-        h = (self.opcode << 9) | (rm << 6) | (rn << 3) | rd
-        return u16(h)
+        at[3:6] = rn
+        at[6:9] = rm
+        at[9:16] = self.opcode
+        #h = (self.opcode << 9) | (rm << 6) | (rn << 3) | rd
+        #return u16(h)
+        return at.encode()
 
     def __repr__(self):
         return '{} {}, {}, {}'.format(self.mnemonic, self.rd, self.rn, self.rm)
@@ -476,11 +530,15 @@
         return cls(im.dst[0], im.src[0])
 
     def encode(self):
-        Rd = self.rd.num & 0x7
+        at = ArmToken()
+        at.rd = self.rd.num & 0x7
         D = (self.rd.num >> 3) & 0x1
         Rm = self.rm.num
         opcode = 0b01000110
-        return u16((opcode << 8) | (D << 7) |(Rm << 3) | Rd)
+        at[8:16] = opcode
+        at[3:7] = Rm
+        at[7] = D
+        return at.encode() # u16((opcode << 8) | (D << 7) |(Rm << 3) | Rd)
 
     def __repr__(self):
         return '{} {}, {}'.format(self.mnemonic, self.rd, self.rm)
@@ -501,11 +559,14 @@
         return cls(im.src[0], im.dst[0])
 
     def encode(self):
+        at = ArmToken()
         rn = self.rn.num
-        rdm = self.rdm.num
+        at.rd = self.rdm.num
         opcode = 0b0100001101
-        h = (opcode << 6) | (rn << 3) | rdm
-        return u16(h)
+        #h = (opcode << 6) | (rn << 3) | rdm
+        at[6:16] = opcode
+        at[3:6] = rn
+        return at.encode()
 
     def __repr__(self):
         return '{} {}, {}'.format(self.mnemonic, self.rn, self.rdm)
@@ -526,10 +587,12 @@
         return cls(im.src[0], im.src[1])
 
     def encode(self):
-        rdn = self.rdn.num
+        at = ArmToken()
+        at.rd = self.rdn.num
         rm = self.rm.num
-        h = (self.opcode << 6) | (rm << 3) | rdn
-        return u16(h)
+        at[3:6] = rm
+        at[6:16] = self.opcode
+        return at.encode()
 
     def __repr__(self):
         return '{} {}, {}'.format(self.mnemonic, self.rdn, self.rm)
@@ -636,7 +699,7 @@
         return [(self.target.name, 'bl_imm11_imm10')]
 
 
-class cond_base_ins_short(jumpBase_ins):
+class cond_base_ins(jumpBase_ins):
     def encode(self):
         imm8 = wrap_negative(self.offset >> 1, 8)
         h = (0b1101 << 12) | (self.cond << 8) | imm8
@@ -646,7 +709,7 @@
         return [(self.target.name, 'rel8')]
 
 
-class cond_base_ins(jumpBase_ins):
+class cond_base_ins_long(jumpBase_ins):
     """ Encoding T3 """
     def encode(self):
         j1 = 1 # TODO: what do these mean?
--- a/python/target/arminstructionselector.py	Mon Feb 17 20:41:30 2014 +0100
+++ b/python/target/arminstructionselector.py	Wed Feb 19 22:32:15 2014 +0100
@@ -1,5 +1,5 @@
 import os
-from ppci import ir
+from ppci import ir, same_dir
 from ppci.irmach import AbstractInstruction as makeIns
 from ppci.ir2tree import makeTree
 import pyburg
@@ -12,7 +12,7 @@
 from .basetarget import Imm8, Imm7, Imm3
 
 # Import BURG spec for arm:
-spec_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'arm.brg')
+spec_file = same_dir(__file__, 'arm.brg')
 arm_matcher = pyburg.load_as_module(spec_file)
 
 
@@ -75,16 +75,16 @@
             self.emit(Nop(), src=[x])
         elif isinstance(s, ir.Jump):
             tgt = self.targets[s.target]
-            self.emit(B(LabelRef(s.target.name)), jumps=[tgt])
+            self.emit(B(LabelRef(ir.label_name(s.target))), jumps=[tgt])
         elif isinstance(s, ir.CJump):
             a = self.munchExpr(s.a)
             b = self.munchExpr(s.b)
             self.emit(Cmp, src=[a, b])
             ntgt = self.targets[s.lab_no]
             ytgt = self.targets[s.lab_yes]
-            jmp_ins = makeIns(B(LabelRef(s.lab_no.name)), jumps=[ntgt])
+            jmp_ins = makeIns(B(LabelRef(ir.label_name(s.lab_no))), jumps=[ntgt])
             opnames = {'<': Blt, '>':Bgt, '==':Beq, '!=':Bne}
-            op = opnames[s.cond](LabelRef(s.lab_yes.name))
+            op = opnames[s.cond](LabelRef(ir.label_name(s.lab_yes)))
             self.emit(op, jumps=[ytgt, jmp_ins])  # Explicitely add fallthrough
             self.emit2(jmp_ins)
         else:
--- a/python/target/instructionselector.py	Mon Feb 17 20:41:30 2014 +0100
+++ b/python/target/instructionselector.py	Wed Feb 19 22:32:15 2014 +0100
@@ -21,6 +21,7 @@
     def newTmp(self):
         return self.temps.__next__()
 
+
     def munchFunction(self, f, frame):
         # Entry point for instruction selection
         assert isinstance(f, ir.Function)
@@ -29,7 +30,7 @@
         self.frame = frame
         # First define labels:
         for bb in f.Blocks:
-            itgt = makeIns(target.Label(bb.name))
+            itgt = makeIns(target.Label(ir.label_name(bb)))
             self.targets[bb] = itgt
         # Generate code for all blocks:
         for bb in f.Blocks:
--- a/python/zcc.py	Mon Feb 17 20:41:30 2014 +0100
+++ b/python/zcc.py	Wed Feb 19 22:32:15 2014 +0100
@@ -97,7 +97,9 @@
         for i in inputs:
             task = self.load_dict(i)
             objs.append(task.output)
-        self.runner.add_task(Link(objs, None))
+        layout = value['layout']
+        output = self.relpath(value['output'])
+        self.runner.add_task(Link(objs, layout, output))
 
     def handle_apps(self, value):
         for a in value:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/testbintools.py	Wed Feb 19 22:32:15 2014 +0100
@@ -0,0 +1,86 @@
+import unittest
+import sys
+from target.arminstructions import ArmToken
+from ppci.linker import Linker
+from ppci.objectfile import ObjectFile
+from ppci import CompilerError
+
+
+class TokenTestCase(unittest.TestCase):
+    def testSetBits(self):
+        at = ArmToken()
+        at[2:4] = 0b11
+        self.assertEqual(0xc, at.bit_value)
+
+    def testSetBits(self):
+        at = ArmToken()
+        at[4:8] = 0b1100
+        self.assertEqual(0xc0, at.bit_value)
+
+
+class LinkerTestCase(unittest.TestCase):
+    def testUndefinedReference(self):
+        l = Linker()
+        o1 = ObjectFile()
+        o1.get_section('.text')
+        o1.add_relocation('undefined_sym', 0, 'rel8', '.text')
+        o2 = ObjectFile()
+        with self.assertRaises(CompilerError):
+            o3 = l.link([o1, o2])
+
+    def testDuplicateSymbol(self):
+        l = Linker()
+        o1 = ObjectFile()
+        o1.get_section('.text')
+        o1.add_symbol('a', 0, '.text')
+        o2 = ObjectFile()
+        o2.get_section('.text')
+        o2.add_symbol('a', 0, '.text')
+        with self.assertRaises(CompilerError):
+            o3 = l.link([o1, o2])
+
+    def testRel8Relocation(self):
+        l = Linker()
+        o1 = ObjectFile()
+        o1.get_section('.text').add_data(bytes([0]*100))
+        o1.add_relocation('a', 0, 'rel8', '.text')
+        o2 = ObjectFile()
+        o2.get_section('.text').add_data(bytes([0]*100))
+        o2.add_symbol('a', 24, '.text')
+        o3 = l.link([o1, o2])
+
+    def testSymbolValues(self):
+        l = Linker()
+        o1 = ObjectFile()
+        o1.get_section('.text').add_data(bytes([0]*108))
+        o1.add_symbol('b', 24, '.text')
+        o2 = ObjectFile()
+        o2.get_section('.text').add_data(bytes([0]*100))
+        o2.add_symbol('a', 2, '.text')
+        o3 = l.link([o1, o2])
+        self.assertEqual(110, o3.find_symbol('a').value)
+        self.assertEqual(24, o3.find_symbol('b').value)
+        self.assertEqual(208, o3.get_section('.text').Size)
+
+    def testMemoryLayout(self):
+        l = Linker()
+        memory_layout = {'.text': 0x08000000, '.data':0x20000000}
+        o1 = ObjectFile()
+        o1.get_section('.text').add_data(bytes([0]*108))
+        o1.add_symbol('b', 24, '.text')
+        o2 = ObjectFile()
+        o2.get_section('.text').add_data(bytes([0]*100))
+        o2.get_section('.data').add_data(bytes([0]*100))
+        o2.add_symbol('a', 2, '.data')
+        o2.add_symbol('c', 2, '.text')
+        o3 = l.link([o1, o2], layout=memory_layout)
+        self.assertEqual(0x20000000+2, o3.find_symbol('a').value)
+        self.assertEqual(0x08000000+24, o3.find_symbol('b').value)
+        self.assertEqual(0x08000000+110, o3.find_symbol('c').value)
+        self.assertEqual(208, o3.get_section('.text').Size)
+        self.assertEqual(100, o3.get_section('.data').Size)
+
+
+if __name__ == '__main__':
+    unittest.main()
+    sys.exit()
--- a/test/testzcc.py	Mon Feb 17 20:41:30 2014 +0100
+++ b/test/testzcc.py	Wed Feb 19 22:32:15 2014 +0100
@@ -34,6 +34,7 @@
 
     def callZcc(self, arg_list):
         parser = zcc.make_parser()
+        arg_list = ['--log', 'warn'] + arg_list
         args = parser.parse_args(arg_list)
         self.assertEqual(0, zcc.main(args))
 
@@ -53,6 +54,18 @@
         recipe = os.path.join(testdir, '..', 'kernel', 'recipe.yaml')
         self.buildRecipe(recipe)
 
+    def testKernelBuildsEqualTwice(self):
+        """ Build kernel two times and check the output is equal """
+        recipe = os.path.join(testdir, '..', 'kernel', 'recipe.yaml')
+        bin_filename = os.path.join(testdir, '..', 'kernel', 'kernel.bin')
+        self.buildRecipe(recipe)
+        with open(bin_filename, 'rb') as f:
+            a = f.read()
+        self.buildRecipe(recipe)
+        with open(bin_filename, 'rb') as f:
+            b = f.read()
+        self.assertSequenceEqual(a, b)
+
     def testUser(self):
         """ Build userspace using zcc: """
         recipe = os.path.join(testdir, '..', 'user', 'recipe.yaml')
--- a/user/recipe.yaml	Mon Feb 17 20:41:30 2014 +0100
+++ b/user/recipe.yaml	Wed Feb 19 22:32:15 2014 +0100
@@ -7,19 +7,8 @@
         includes: []
         machine: arm
         output: kernel.elf2
+    layout:
+         code: 0x0
+         data: 0x20000000
+    output: 'hello.bin'
 
- - link:
-    inputs:
-        - compile:
-           sources: [lib.c3, ipc.c3, screen.c3]
-           includes: []
-           machine: arm
-           output: kernel.elf2
-
- - link:
-    inputs:
-      - compile:
-         sources: [lib.c3, ipc.c3, console.c3]
-         includes: []
-         machine: arm
-         output: kernel.elf2