# HG changeset patch # User Windel Bouwman # Date 1401358417 -7200 # Node ID c0d9837acde8867680f15f61abfd53da3631be89 # Parent 5d03c10fe19dc6393a992f9e7111f9ec2a0881f7 x86 target refactor diff -r 5d03c10fe19d -r c0d9837acde8 kernel/arch/qemu_vexpress/layout.mmap --- a/kernel/arch/qemu_vexpress/layout.mmap Thu May 29 10:47:28 2014 +0200 +++ b/kernel/arch/qemu_vexpress/layout.mmap Thu May 29 12:13:37 2014 +0200 @@ -4,6 +4,8 @@ SECTION(code) ALIGN(0x4000) SECTION(mem_tables) + ALIGN(0x4000) + SECTION(ramdisk) } MEMORY ram LOCATION=0x20000 SIZE=0x10000 { diff -r 5d03c10fe19d -r c0d9837acde8 kernel/build.xml --- a/kernel/build.xml Thu May 29 10:47:28 2014 +0200 +++ b/kernel/build.xml Thu May 29 12:13:37 2014 +0200 @@ -11,6 +11,9 @@ + + @@ -23,7 +26,7 @@ + objects="henkie.o;vexp.o;start.o;ramdisk.o" /> > (p*8)) & 0xFF for p in range(8) ] + +def imm32(x): + """ represent 32 bits integer in little endian 4 bytes""" + if x < 0: + x = x + (1 << 32) + x = x & 0xFFFFFFFF + return [ (x >> (p*8)) & 0xFF for p in range(4) ] + + +def imm8(x): + if x < 0: + x = x + (1 << 8) + x = x & 0xFF + return [ x ] + + +class ModRmToken(Token): + """ Construct the modrm byte from its components """ + def __init__(self, mod=0, rm=0, reg=0): + super().__init__(8) + assert(mod <= 3) + assert(rm <= 7) + assert(reg <= 7) + self.mod = mod + self.rm = rm + self.reg = reg + + mod = bit_range(6, 8) + rm = bit_range(0, 3) + reg = bit_range(3, 6) + + def encode(self): + return u8(self.bit_value) + + +class RexToken(Token): + """ Create a REX prefix byte """ + def __init__(self, w=0, r=0, x=0, b=0): + super().__init__(8) + assert(w <= 1) + assert(r <= 1) + assert(x <= 1) + assert(b <= 1) + self.w = w + self.r = r + self.x = x + self.b = b + self.set_bit(6, 1) + + w = bit_range(3, 4) + r = bit_range(2, 3) + x = bit_range(1, 2) + b = bit_range(0, 1) + + def encode(self): + return u8(self.bit_value) + + +def sib(ss=0, index=0, base=0): + assert(ss <= 3) + assert(index <= 7) + assert(base <= 7) + return (ss << 6) | (index << 3) | base + +tttn = {'L':0xc,'G':0xf,'NE':0x5,'GE':0xd,'LE':0xe, 'E':0x4} + +# Actual instructions: +def nearjump(distance, condition=None): + """ jmp imm32 """ + lim = (1<<30) + if abs(distance) > lim: + Error('near jump cannot jump over more than {0} bytes'.format(lim)) + if condition: + if distance < 0: + distance -= 6 # Skip own instruction + opcode = 0x80 | tttn[condition] # Jcc imm32 + return [0x0F, opcode] + imm32(distance) + else: + if distance < 0: + distance -= 5 # Skip own instruction + return [ 0xE9 ] + imm32(distance) + +def shortjump(distance, condition=None): + """ jmp imm8 """ + lim = 118 + if abs(distance) > lim: + Error('short jump cannot jump over more than {0} bytes'.format(lim)) + if distance < 0: + distance -= 2 # Skip own instruction + if condition: + opcode = 0x70 | tttn[condition] # Jcc rel8 + else: + opcode = 0xeb # jmp rel8 + return [opcode] + imm8(distance) + +# Helper that determines jump type: +def reljump(distance): + if abs(distance) < 110: + return shortjump(distance) + else: + return nearjump(distance) + + +class Push(Instruction): + def __init__(self, reg): + assert(reg in regs64), str(reg) + self.reg = reg + + def encode(self): + code = [] + if self.reg.rexbit == 1: + code.append(0x41) + code.append(0x50 + self.reg.regbits) + return bytes(code) + + +class Pop(Instruction): + def __init__(self, reg): + assert(reg in regs64), str(reg) + self.reg = reg + + def encode(self): + code = [] + if self.reg.rexbit == 1: + code.append(0x41) + code.append(0x58 + self.reg.regbits) + return bytes(code) + + +def pop(reg): + if reg in regs64: + if rexbit[reg] == 1: + rexprefix = rex(b=1) + opcode = 0x58 + regs64[reg] + return [rexprefix, opcode] + else: + opcode = 0x58 + regs64[reg] + return [ opcode ] + else: + Error('pop for {0} not implemented'.format(reg)) + +def INT(number): + opcode = 0xcd + return [opcode] + imm8(number) + +def syscall(): + return [0x0F, 0x05] + +def call(distance): + if type(distance) is int: + return [0xe8]+imm32(distance) + elif type(distance) is str and distance in regs64: + reg = distance + opcode = 0xFF # 0xFF /2 == call r/m64 + mod_rm = modrm(mod=3, reg=2, rm=regs64[reg]) + if rexbit[reg] == 1: + rexprefix = rex(b=rexbit[reg]) + return [rexprefix, opcode, mod_rm] + else: + return [opcode, mod_rm] + else: + Error('Cannot call to {0}'.format(distance)) + + +class Ret(Instruction): + def __init__(self): + pass + + def encode(self): + return [ 0xc3 ] + + +class Inc(Instruction): + def __init__(self, reg): + assert(reg in regs64), str(reg) + self.rex = RexToken(w=1, b=reg.rexbit) + self.opcode = 0xff + self.mod_rm = ModRmToken(mod=3, rm=reg.regbits) + + def encode(self): + code = bytes([self.opcode]) + return self.rex.encode() + code + self.mod_rm.encode() + + +def prepost8(r8, rm8): + assert(r8 in regs8) + pre = [] + if type(rm8) is list: + # TODO: merge mem access with prepost for 64 bits + if len(rm8) == 1: + base, = rm8 + if type(base) is str and base in regs64: + assert(not base in ['rbp', 'rsp', 'r12', 'r13']) + mod_rm = modrm(mod=0, rm=regs64[base], reg=regs8[r8]) + if rexbit[base] == 1: + pre.append(rex(b=1)) + post = [mod_rm] + else: + Error('One arg of type {0} not implemented'.format(base)) + elif len(rm8) == 2: + base, offset = rm8 + assert(type(offset) is int) + assert(base in regs64) + + if base == 'rsp' or base == 'r12': + Error('Cannot use rsp or r12 as base yet') + if rexbit[base] == 1: + pre.append( rex(b=1) ) + mod_rm = modrm(mod=1, rm=regs64[base], reg=regs8[r8]) + post = [mod_rm] + imm8(offset) + else: + Error('not supporting prepost8 with list len {0}'.format(len(rm8))) + else: + Error('Not supporting move with reg8 {0}'.format(r8)) + return pre, post + +def prepost(r64, rm64): + assert(r64 in regs64) + if type(rm64) is list: + if len(rm64) == 3: + base, index, disp = rm64 + assert(base in regs64) + assert(index in regs64) + assert(type(disp) is int) + # Assert that no special cases are used: + # TODO: swap base and index to avoid special cases + # TODO: exploit special cases and make better code + assert(index != 'rsp') + + rexprefix = rex(w=1, r=rexbit[r64], x=rexbit[index], b=rexbit[base]) + # mod=1 and rm=4 indicates a SIB byte: [--][--]+imm8 + mod_rm = modrm(mod=1, rm=4, reg=regs64[r64]) + si_b = sib(ss=0, index=regs64[index], base=regs64[base]) + return [rexprefix], [mod_rm, si_b] + imm8(disp) + elif len(rm64) == 2: + base, offset = rm64 + assert(type(offset) is int) + if base == 'RIP': + # RIP pointer relative addressing mode! + rexprefix = rex(w=1, r=rexbit[r64]) + mod_rm = modrm(mod=0, rm=5, reg=regs64[r64]) + return [rexprefix], [mod_rm] + imm32(offset) + else: + assert(base in regs64) + + if base == 'rsp' or base == 'r12': + # extended function that uses SIB byte + rexprefix = rex(w=1, r=rexbit[r64], b=rexbit[base]) + # rm=4 indicates a SIB byte follows + mod_rm = modrm(mod=1, rm=4, reg=regs64[r64]) + # index=4 indicates that index is not used + si_b = sib(ss=0, index=4, base=regs64[base]) + return [rexprefix], [mod_rm, si_b] + imm8(offset) + else: + rexprefix = rex(w=1, r=rexbit[r64], b=rexbit[base]) + mod_rm = modrm(mod=1, rm=regs64[base], reg=regs64[r64]) + return [rexprefix], [mod_rm] + imm8(offset) + elif len(rm64) == 1: + offset = rm64[0] + if type(offset) is int: + rexprefix = rex(w=1, r=rexbit[r64]) + mod_rm = modrm(mod=0, rm=4,reg=regs64[r64]) + si_b = sib(ss=0, index=4,base=5) # 0x25 + return [rexprefix], [mod_rm, si_b] + imm32(offset) + else: + Error('Memory reference of type {0} not implemented'.format(offset)) + else: + Error('Memory reference not implemented') + elif rm64 in regs64: + rexprefix = rex(w=1, r=rexbit[r64], b=rexbit[rm64]) + mod_rm = modrm(3, rm=regs64[rm64], reg=regs64[r64]) + return [rexprefix], [mod_rm] + + +def leareg64(rega, m): + opcode = 0x8d # lea r64, m + pre, post = prepost(rega, m) + return pre + [opcode] + post + + +class Mov1(Instruction): + """ Mov r64 to r64 """ + def __init__(self, dst, src): + assert src in regs64, str(src) + assert dst in regs64, str(dst) + self.rex = RexToken(w=1, r=dst.rexbit, b=src.rexbit) + self.mod_rm = ModRmToken(mod=3, rm=dst.regbits, reg=src.regbits) + + def encode(self): + opcode = 0x89 # mov r/m64, r64 + code = bytes([opcode]) + return self.rex.encode() + code + self.mod_rm.encode() + + +def Mov(dst, src): + if type(src) is int: + pre = [rex(w=1, b=rexbit[rega])] + opcode = 0xb8 + regs64[rega] + post = imm64(regb) + elif type(src) is X86Register: + return Mov1(dst, src) + elif type(src) is str: + if rega in regs64: + opcode = 0x8b # mov r64, r/m64 + pre, post = prepost(rega, regb) + else: + raise Exception('Unknown register {0}'.format(rega)) + else: + raise Exception('Move of this kind {0}, {1} not implemented'.format(rega, regb)) + return pre + [opcode] + post + + +def Xor(rega, regb): + return Xor1(rega, regb) + + +class Xor1(Instruction): + def __init__(self, a, b): + self.rex = RexToken(w=1, r=b.rexbit, b=a.rexbit) + self.mod_rm = ModRmToken(mod=3, rm=a.regbits, reg=b.regbits) + + def encode(self): + opcode = 0x31 # XOR r/m64, r64 + # Alternative is 0x33 XOR r64, r/m64 + code = bytes([opcode]) + return self.rex.encode() + code + self.mod_rm.encode() + + +# integer arithmatic: +def addreg64(rega, regb): + if regb in regs64: + pre, post = prepost(regb, rega) + opcode = 0x01 # ADD r/m64, r64 + return pre + [opcode] + post + elif type(regb) is int: + if regb < 100: + rexprefix = rex(w=1, b=rexbit[rega]) + opcode = 0x83 # add r/m, imm8 + mod_rm = modrm(3, rm=regs64[rega], reg=0) + return [rexprefix, opcode, mod_rm]+imm8(regb) + elif regb < (1<<31): + rexprefix = rex(w=1, b=rexbit[rega]) + opcode = 0x81 # add r/m64, imm32 + mod_rm = modrm(3, rm=regs64[rega], reg=0) + return [rexprefix, opcode, mod_rm]+imm32(regb) + else: + Error('Constant value too large!') + else: + Error('unknown second operand!'.format(regb)) + +def subreg64(rega, regb): + if regb in regs64: + pre, post = prepost(regb, rega) + opcode = 0x29 # SUB r/m64, r64 + return pre + [opcode] + post + elif type(regb) is int: + if regb < 100: + rexprefix = rex(w=1, b=rexbit[rega]) + opcode = 0x83 # sub r/m, imm8 + mod_rm = modrm(3, rm=regs64[rega], reg=5) + return [rexprefix, opcode, mod_rm]+imm8(regb) + elif regb < (1<<31): + rexprefix = rex(w=1, b=rexbit[rega]) + opcode = 0x81 # sub r/m64, imm32 + mod_rm = modrm(3, rm=regs64[rega], reg=5) + return [rexprefix, opcode, mod_rm]+imm32(regb) + else: + Error('Constant value too large!') + + else: + Error('unknown second operand!'.format(regb)) + +def idivreg64(reg): + rexprefix = rex(w=1, b=rexbit[reg]) + opcode = 0xf7 # IDIV r/m64 + mod_rm = modrm(3, rm=regs64[reg], reg=7) + return [rexprefix, opcode, mod_rm] + +def imulreg64_rax(reg): + rexprefix = rex(w=1, b=rexbit[reg]) + opcode = 0xf7 # IMUL r/m64 + mod_rm = modrm(3, rm=regs64[reg], reg=5) + return [rexprefix, opcode, mod_rm] + +def imulreg64(rega, regb): + pre, post = prepost(rega, regb) + opcode = 0x0f # IMUL r64, r/m64 + opcode2 = 0xaf + return pre + [opcode, opcode2] + post + + +def cmpreg64(rega, regb): + if regb in regs64: + pre, post = prepost(regb, rega) + opcode = 0x39 # CMP r/m64, r64 + return pre + [opcode] + post + elif type(regb) is int: + rexprefix = rex(w=1, b=rexbit[rega]) + opcode = 0x83 # CMP r/m64, imm8 + mod_rm = modrm(3, rm=regs64[rega], reg=7) + return [rexprefix, opcode, mod_rm] + imm8(regb) + else: + Error('not implemented cmp64') diff -r 5d03c10fe19d -r c0d9837acde8 python/ppci/target/x86/registers.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/ppci/target/x86/registers.py Thu May 29 12:13:37 2014 +0200 @@ -0,0 +1,52 @@ +""" + Contains register definitions for x86 target. +""" +from ..basetarget import Register + + +class X86Register(Register): + def __init__(self, num, name): + super().__init__(name) + self.num = num + + def __repr__(self): + return 'x86reg {}'.format(self.name) + + @property + def rexbit(self): + return (self.num >> 3) & 0x1 + + @property + def regbits(self): + return self.num & 0x7 + +# Calculation of the rexb bit: +# rexbit = {'rax': 0, 'rcx':0, 'rdx':0, 'rbx': 0, 'rsp': 0, 'rbp': 0, 'rsi':0, +# 'rdi':0,'r8':1,'r9':1,'r10':1,'r11':1,'r12':1,'r13':1,'r14':1,'r15':1} + +# regs64 = {'rax': 0,'rcx':1,'rdx':2,'rbx':3,'rsp':4,'rbp':5,'rsi':6,'rdi':7, +# 'r8':0,'r9':1,'r10':2,'r11':3,'r12':4,'r13':5,'r14':6,'r15':7} +# regs32 = {'eax': 0, 'ecx':1, 'edx':2, 'ebx': 3, 'esp': 4, 'ebp': 5, 'esi':6, +# 'edi':7} +# regs8 = {'al':0,'cl':1,'dl':2,'bl':3,'ah':4,'ch':5,'dh':6,'bh':7} +rax = X86Register(0, 'rax') +rcx = X86Register(1, 'rcx') +rdx = X86Register(2, 'rdx') +rbx = X86Register(3, 'rbx') +rsp = X86Register(4, 'rsp') +rbp = X86Register(5, 'rbp') +rsi = X86Register(6, 'rsi') +rdi = X86Register(7, 'rdi') + +r8 = X86Register(8, 'r8') +r9 = X86Register(9, 'r9') +r10 = X86Register(10, 'r10') +r11 = X86Register(11, 'r11') +r12 = X86Register(12, 'r12') +r13 = X86Register(13, 'r13') +r14 = X86Register(14, 'r14') +r15 = X86Register(15, 'r15') + +low_regs = {rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi} + +regs64 = {r8, r9, r10, r11, r12, r13, r14, r15} | low_regs diff -r 5d03c10fe19d -r c0d9837acde8 python/ppci/target/x86/target_x86.py --- a/python/ppci/target/x86/target_x86.py Thu May 29 10:47:28 2014 +0200 +++ b/python/ppci/target/x86/target_x86.py Thu May 29 12:13:37 2014 +0200 @@ -1,66 +1,59 @@ -from target import Register, Instruction, Target - -class x86Register(Register): - def __init__(self, name): - self.name = name - -class REG16(x86Register): - pass - -def addRegs(cls, names): - for name in names: - r = cls(name) - globals()[name] = r - -addRegs(REG16, ['ax', 'bx', 'cx']) +from ..basetarget import Target +from ...assembler import BaseAssembler +from .registers import rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi +from .registers import r8, r9, r10, r11, r12, r13, r14, r15, regs64 +from .instructions import Mov, Inc, Xor, Push, Pop -regs = """ -ax; reg16 -""" -class MO: - def __init__(self): - pass - -instrs = """ -add; 0x0; mem8/reg8; reg8 -""" - -# machine op table: -mot = [] - -for i in instrs.split('\n'): - i = i.strip() - if i: - print('INS:', i) - mnemonic, opcode, op1, op2 = [a.strip() for a in i.split(';')] - print(op1.split('/'), op2.split('/')) +class X86Assembler(BaseAssembler): + def __init__(self, target): + super().__init__(target) + self.make_parser() -print(mot) +class X86Target(Target): + """ x86 target containing assembler, linker""" + def __init__(self): + super().__init__('x86') -# Test first with these 3 instructions: -""" -mov reg64, reg64 : opcode=0x89 -xor reg64, reg64 : opcode=0x31 -inc reg64 : opcode=0xff -""" + for reg in regs64: + self.add_keyword(reg.name) -class x86Machine: - def __init__(self): - self.table = [] - self.table.append((0x0, 'add', 'reg8/mem8, reg8')) - self.table.append((0x1, 'add', 'reg16/mem16/reg32/mem32, reg16/reg32')) - self.table.append((0x2, 'add', 'reg8, reg8/mem8')) - def forMnemonic(self, m): - return [i for i in self.table if i[1] == m] - def emit(self, m, ops): - print(m, ops) - ops = self.forMnemonic(m) - print(ops) + self.add_rule('reg', ['rax'], lambda rhs: rax) + self.add_rule('reg', ['rcx'], lambda rhs: rcx) + self.add_rule('reg', ['rdx'], lambda rhs: rdx) + self.add_rule('reg', ['rbx'], lambda rhs: rbx) + self.add_rule('reg', ['rsp'], lambda rhs: rsp) + self.add_rule('reg', ['rbp'], lambda rhs: rbp) + self.add_rule('reg', ['rsi'], lambda rhs: rsi) + self.add_rule('reg', ['rdi'], lambda rhs: rdi) + self.add_rule('reg', ['r8'], lambda rhs: r8) + self.add_rule('reg', ['r9'], lambda rhs: r9) + self.add_rule('reg', ['r10'], lambda rhs: r10) + self.add_rule('reg', ['r11'], lambda rhs: r11) + self.add_rule('reg', ['r12'], lambda rhs: r12) + self.add_rule('reg', ['r13'], lambda rhs: r13) + self.add_rule('reg', ['r14'], lambda rhs: r14) + self.add_rule('reg', ['r15'], lambda rhs: r15) + self.add_keyword('mov') + self.add_instruction(['mov', 'reg', ',', 'reg'], + lambda rhs: Mov(rhs[1], rhs[3])) -if __name__ == '__main__': - m = x86Machine() - m.emit('add', [ax, cx]) - m.emit('mov', [bx, 1337]) + self.add_keyword('xor') + self.add_instruction(['xor', 'reg', ',', 'reg'], + lambda rhs: Xor(rhs[1], rhs[3])) + + self.add_keyword('inc') + self.add_instruction(['inc', 'reg'], + lambda rhs: Inc(rhs[1])) + + self.add_keyword('push') + self.add_instruction(['push', 'reg'], + lambda rhs: Push(rhs[1])) + + self.add_keyword('pop') + self.add_instruction(['pop', 'reg'], + lambda rhs: Pop(rhs[1])) + + self.assembler = X86Assembler(self) diff -r 5d03c10fe19d -r c0d9837acde8 python/ppci/target/x86/x86.py --- a/python/ppci/target/x86/x86.py Thu May 29 10:47:28 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +0,0 @@ -import ppci -import ir - -class X86CodeGenSimple: - """ - Inefficient code generation, assume stack machine - backend - """ - def __init__(self, diag): - self.diag = diag - - def emit(self, i): - self.asm.append(i) - - def genBin(self, ir): - self.asm = [] - self.genModule(ir) - return self.asm - - def genModule(self, ir): - for f in ir.Functions: - self.genFunction(f) - def genFunction(self, f): - self.emit('global {0}'.format(f.name)) - self.emit('{0}:'.format(f.name)) - self.emit('jmp {0}'.format(f.entry.name)) - for bb in f.BasicBlocks: - self.genBB(bb) - def genBB(self, bb): - self.emit('{0}:'.format(bb.name)) - for i in bb.Instructions: - self.genIns(i) - def genIns(self, i): - if type(i) is ir.BinaryOperator: - ops = {'+':'add', '-':'sub', '*':'mul'} - if i.operation in ops: - i.result.reg = 'rax' - i.value1.reg = 'rbx' - i.value2.reg = 'rbx' - self.emit('mov {0}, {1}'.format(i.result.reg, i.value1.reg)) - self.emit('{0} {1}, {2}'.format(ops[i.operation], i.result.reg, i.value2.reg)) - else: - raise NotImplementedError('op {0}'.format(i.operation)) - elif type(i) is ir.Load: - self.emit('mov {0}, [{1}]'.format(i.value, i.location)) - elif type(i) is ir.Return: - self.emit('ret') - elif type(i) is ir.Call: - self.emit('call') - elif type(i) is ir.ImmLoad: - self.emit('mov {0}, {1}'.format(i.target, i.value)) - elif type(i) is ir.Store: - self.emit('mov [{0}], {1}'.format(i.location, i.value)) - elif type(i) is ir.ConditionalBranch: - self.emit('cmp {0}, {1}'.format(i.a, i.b)) - jmps = {'>':'jg', '<':'jl', '==':'je'} - if i.cond in jmps: - j = jmps[i.cond] - self.emit('{0} {1}'.format(j, i.lab1.name)) - else: - raise NotImplementedError('condition {0}'.format(i.cond)) - self.emit('jmp {0}'.format(i.lab2.name)) - elif type(i) is ir.Branch: - self.emit('jmp {0}'.format(i.target.name)) - elif type(i) is ir.Alloc: - pass - else: - raise NotImplementedError('{0}'.format(i)) - diff -r 5d03c10fe19d -r c0d9837acde8 python/ppci/target/x86/x86_2.py --- a/python/ppci/target/x86/x86_2.py Thu May 29 10:47:28 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,356 +0,0 @@ -""" - X86 target descriptions and encodings. - -""" - -from target import Register, Instruction, Target, Imm8, Label, Imm3, LabelRef - - -modrm = {'rax': 0, 'rbx': 1} - -# Table 3.1 of the intel manual: -# use REX.W on the table below: -regs64 = {'rax': 0,'rcx':1,'rdx':2,'rbx':3,'rsp':4,'rbp':5,'rsi':6,'rdi':7,'r8':0,'r9':1,'r10':2,'r11':3,'r12':4,'r13':5,'r14':6,'r15':7} -regs32 = {'eax': 0, 'ecx':1, 'edx':2, 'ebx': 3, 'esp': 4, 'ebp': 5, 'esi':6, 'edi':7} -regs8 = {'al':0,'cl':1,'dl':2,'bl':3,'ah':4,'ch':5,'dh':6,'bh':7} - -# Calculation of the rexb bit: -rexbit = {'rax': 0, 'rcx':0, 'rdx':0, 'rbx': 0, 'rsp': 0, 'rbp': 0, 'rsi':0, 'rdi':0,'r8':1,'r9':1,'r10':1,'r11':1,'r12':1,'r13':1,'r14':1,'r15':1} - -# Helper functions: -def imm64(x): - """ represent 64 bits integer in little endian 8 bytes""" - if x < 0: - x = x + (1 << 64) - x = x & 0xFFFFFFFFFFFFFFFF - return [ (x >> (p*8)) & 0xFF for p in range(8) ] - -def imm32(x): - """ represent 32 bits integer in little endian 4 bytes""" - if x < 0: - x = x + (1 << 32) - x = x & 0xFFFFFFFF - return [ (x >> (p*8)) & 0xFF for p in range(4) ] - -def imm8(x): - if x < 0: - x = x + (1 << 8) - x = x & 0xFF - return [ x ] - -def modrm(mod=0, rm=0, reg=0): - """ Construct the modrm byte from its components """ - assert(mod <= 3) - assert(rm <= 7) - assert(reg <= 7) - return (mod << 6) | (reg << 3) | rm - -def rex(w=0, r=0, x=0, b=0): - """ Create a REX prefix byte """ - assert(w <= 1) - assert(r <= 1) - assert(x <= 1) - assert(b <= 1) - return 0x40 | (w<<3) | (r<<2) | (x<<1) | b - -def sib(ss=0, index=0, base=0): - assert(ss <= 3) - assert(index <= 7) - assert(base <= 7) - return (ss << 6) | (index << 3) | base - -tttn = {'L':0xc,'G':0xf,'NE':0x5,'GE':0xd,'LE':0xe, 'E':0x4} - -# Actual instructions: -def nearjump(distance, condition=None): - """ jmp imm32 """ - lim = (1<<30) - if abs(distance) > lim: - Error('near jump cannot jump over more than {0} bytes'.format(lim)) - if condition: - if distance < 0: - distance -= 6 # Skip own instruction - opcode = 0x80 | tttn[condition] # Jcc imm32 - return [0x0F, opcode] + imm32(distance) - else: - if distance < 0: - distance -= 5 # Skip own instruction - return [ 0xE9 ] + imm32(distance) - -def shortjump(distance, condition=None): - """ jmp imm8 """ - lim = 118 - if abs(distance) > lim: - Error('short jump cannot jump over more than {0} bytes'.format(lim)) - if distance < 0: - distance -= 2 # Skip own instruction - if condition: - opcode = 0x70 | tttn[condition] # Jcc rel8 - else: - opcode = 0xeb # jmp rel8 - return [opcode] + imm8(distance) - -# Helper that determines jump type: -def reljump(distance): - if abs(distance) < 110: - return shortjump(distance) - else: - return nearjump(distance) - -def push(reg): - if reg in regs64: - if rexbit[reg] == 1: - return [0x41, 0x50 + regs64[reg]] - else: - return [0x50 + regs64[reg]] - else: - Error('push for {0} not implemented'.format(reg)) - -def pop(reg): - if reg in regs64: - if rexbit[reg] == 1: - rexprefix = rex(b=1) - opcode = 0x58 + regs64[reg] - return [rexprefix, opcode] - else: - opcode = 0x58 + regs64[reg] - return [ opcode ] - else: - Error('pop for {0} not implemented'.format(reg)) - -def INT(number): - opcode = 0xcd - return [opcode] + imm8(number) - -def syscall(): - return [0x0F, 0x05] - -def call(distance): - if type(distance) is int: - return [0xe8]+imm32(distance) - elif type(distance) is str and distance in regs64: - reg = distance - opcode = 0xFF # 0xFF /2 == call r/m64 - mod_rm = modrm(mod=3, reg=2, rm=regs64[reg]) - if rexbit[reg] == 1: - rexprefix = rex(b=rexbit[reg]) - return [rexprefix, opcode, mod_rm] - else: - return [opcode, mod_rm] - else: - Error('Cannot call to {0}'.format(distance)) - -def ret(): - return [ 0xc3 ] - -def increg64(reg): - assert(reg in regs64) - rexprefix = rex(w=1, b=rexbit[reg]) - opcode = 0xff - mod_rm = modrm(mod=3, rm=regs64[reg]) - return [rexprefix, opcode, mod_rm] - -def prepost8(r8, rm8): - assert(r8 in regs8) - pre = [] - if type(rm8) is list: - # TODO: merge mem access with prepost for 64 bits - if len(rm8) == 1: - base, = rm8 - if type(base) is str and base in regs64: - assert(not base in ['rbp', 'rsp', 'r12', 'r13']) - mod_rm = modrm(mod=0, rm=regs64[base], reg=regs8[r8]) - if rexbit[base] == 1: - pre.append(rex(b=1)) - post = [mod_rm] - else: - Error('One arg of type {0} not implemented'.format(base)) - elif len(rm8) == 2: - base, offset = rm8 - assert(type(offset) is int) - assert(base in regs64) - - if base == 'rsp' or base == 'r12': - Error('Cannot use rsp or r12 as base yet') - if rexbit[base] == 1: - pre.append( rex(b=1) ) - mod_rm = modrm(mod=1, rm=regs64[base], reg=regs8[r8]) - post = [mod_rm] + imm8(offset) - else: - Error('not supporting prepost8 with list len {0}'.format(len(rm8))) - else: - Error('Not supporting move with reg8 {0}'.format(r8)) - return pre, post - -def prepost(r64, rm64): - assert(r64 in regs64) - if type(rm64) is list: - if len(rm64) == 3: - base, index, disp = rm64 - assert(base in regs64) - assert(index in regs64) - assert(type(disp) is int) - # Assert that no special cases are used: - # TODO: swap base and index to avoid special cases - # TODO: exploit special cases and make better code - assert(index != 'rsp') - - rexprefix = rex(w=1, r=rexbit[r64], x=rexbit[index], b=rexbit[base]) - # mod=1 and rm=4 indicates a SIB byte: [--][--]+imm8 - mod_rm = modrm(mod=1, rm=4, reg=regs64[r64]) - si_b = sib(ss=0, index=regs64[index], base=regs64[base]) - return [rexprefix], [mod_rm, si_b] + imm8(disp) - elif len(rm64) == 2: - base, offset = rm64 - assert(type(offset) is int) - if base == 'RIP': - # RIP pointer relative addressing mode! - rexprefix = rex(w=1, r=rexbit[r64]) - mod_rm = modrm(mod=0, rm=5, reg=regs64[r64]) - return [rexprefix], [mod_rm] + imm32(offset) - else: - assert(base in regs64) - - if base == 'rsp' or base == 'r12': - # extended function that uses SIB byte - rexprefix = rex(w=1, r=rexbit[r64], b=rexbit[base]) - # rm=4 indicates a SIB byte follows - mod_rm = modrm(mod=1, rm=4, reg=regs64[r64]) - # index=4 indicates that index is not used - si_b = sib(ss=0, index=4, base=regs64[base]) - return [rexprefix], [mod_rm, si_b] + imm8(offset) - else: - rexprefix = rex(w=1, r=rexbit[r64], b=rexbit[base]) - mod_rm = modrm(mod=1, rm=regs64[base], reg=regs64[r64]) - return [rexprefix], [mod_rm] + imm8(offset) - elif len(rm64) == 1: - offset = rm64[0] - if type(offset) is int: - rexprefix = rex(w=1, r=rexbit[r64]) - mod_rm = modrm(mod=0, rm=4,reg=regs64[r64]) - si_b = sib(ss=0, index=4,base=5) # 0x25 - return [rexprefix], [mod_rm, si_b] + imm32(offset) - else: - Error('Memory reference of type {0} not implemented'.format(offset)) - else: - Error('Memory reference not implemented') - elif rm64 in regs64: - rexprefix = rex(w=1, r=rexbit[r64], b=rexbit[rm64]) - mod_rm = modrm(3, rm=regs64[rm64], reg=regs64[r64]) - return [rexprefix], [mod_rm] - -def leareg64(rega, m): - opcode = 0x8d # lea r64, m - pre, post = prepost(rega, m) - return pre + [opcode] + post - -def mov(rega, regb): - if type(regb) is int: - pre = [rex(w=1, b=rexbit[rega])] - opcode = 0xb8 + regs64[rega] - post = imm64(regb) - elif type(regb) is str: - if regb in regs64: - opcode = 0x89 # mov r/m64, r64 - pre, post = prepost(regb, rega) - elif regb in regs8: - opcode = 0x88 # mov r/m8, r8 - pre, post = prepost8(regb, rega) - else: - Error('Unknown register {0}'.format(regb)) - elif type(rega) is str: - if rega in regs64: - opcode = 0x8b # mov r64, r/m64 - pre, post = prepost(rega, regb) - else: - Error('Unknown register {0}'.format(rega)) - else: - Error('Move of this kind {0}, {1} not implemented'.format(rega, regb)) - return pre + [opcode] + post - -def xorreg64(rega, regb): - rexprefix = rex(w=1, r=rexbit[regb], b=rexbit[rega]) - opcode = 0x31 # XOR r/m64, r64 - # Alternative is 0x33 XOR r64, r/m64 - mod_rm = modrm(3, rm=regs64[rega], reg=regs64[regb]) - return [rexprefix, opcode, mod_rm] - -# integer arithmatic: -def addreg64(rega, regb): - if regb in regs64: - pre, post = prepost(regb, rega) - opcode = 0x01 # ADD r/m64, r64 - return pre + [opcode] + post - elif type(regb) is int: - if regb < 100: - rexprefix = rex(w=1, b=rexbit[rega]) - opcode = 0x83 # add r/m, imm8 - mod_rm = modrm(3, rm=regs64[rega], reg=0) - return [rexprefix, opcode, mod_rm]+imm8(regb) - elif regb < (1<<31): - rexprefix = rex(w=1, b=rexbit[rega]) - opcode = 0x81 # add r/m64, imm32 - mod_rm = modrm(3, rm=regs64[rega], reg=0) - return [rexprefix, opcode, mod_rm]+imm32(regb) - else: - Error('Constant value too large!') - else: - Error('unknown second operand!'.format(regb)) - -def subreg64(rega, regb): - if regb in regs64: - pre, post = prepost(regb, rega) - opcode = 0x29 # SUB r/m64, r64 - return pre + [opcode] + post - elif type(regb) is int: - if regb < 100: - rexprefix = rex(w=1, b=rexbit[rega]) - opcode = 0x83 # sub r/m, imm8 - mod_rm = modrm(3, rm=regs64[rega], reg=5) - return [rexprefix, opcode, mod_rm]+imm8(regb) - elif regb < (1<<31): - rexprefix = rex(w=1, b=rexbit[rega]) - opcode = 0x81 # sub r/m64, imm32 - mod_rm = modrm(3, rm=regs64[rega], reg=5) - return [rexprefix, opcode, mod_rm]+imm32(regb) - else: - Error('Constant value too large!') - - else: - Error('unknown second operand!'.format(regb)) - -def idivreg64(reg): - rexprefix = rex(w=1, b=rexbit[reg]) - opcode = 0xf7 # IDIV r/m64 - mod_rm = modrm(3, rm=regs64[reg], reg=7) - return [rexprefix, opcode, mod_rm] - -def imulreg64_rax(reg): - rexprefix = rex(w=1, b=rexbit[reg]) - opcode = 0xf7 # IMUL r/m64 - mod_rm = modrm(3, rm=regs64[reg], reg=5) - return [rexprefix, opcode, mod_rm] - -def imulreg64(rega, regb): - pre, post = prepost(rega, regb) - opcode = 0x0f # IMUL r64, r/m64 - opcode2 = 0xaf - return pre + [opcode, opcode2] + post - -def cmpreg64(rega, regb): - if regb in regs64: - pre, post = prepost(regb, rega) - opcode = 0x39 # CMP r/m64, r64 - return pre + [opcode] + post - elif type(regb) is int: - rexprefix = rex(w=1, b=rexbit[rega]) - opcode = 0x83 # CMP r/m64, imm8 - mod_rm = modrm(3, rm=regs64[rega], reg=7) - return [rexprefix, opcode, mod_rm] + imm8(regb) - - else: - Error('not implemented cmp64') - -# Mapping that maps string names to the right functions: -opcodes = {'mov':(mov,2), 'lea':(leareg64,2), 'int':(INT,1), 'syscall':(syscall,0)} - diff -r 5d03c10fe19d -r c0d9837acde8 test/testdiagrameditor.py --- a/test/testdiagrameditor.py Thu May 29 10:47:28 2014 +0200 +++ b/test/testdiagrameditor.py Thu May 29 12:13:37 2014 +0200 @@ -21,6 +21,8 @@ skip_it = True +if 'LCFOSGUITESTS' not in os.environ: + skip_it = True class DiagramEditorTestCase(unittest.TestCase): def setUp(self): diff -r 5d03c10fe19d -r c0d9837acde8 test/testir.py --- a/test/testir.py Thu May 29 10:47:28 2014 +0200 +++ b/test/testir.py Thu May 29 12:13:37 2014 +0200 @@ -70,10 +70,10 @@ self.b.emit(ir.Jump(bb)) self.b.setBlock(bb) v1 = ir.Const(5) - self.b.emit(v1) + # self.b.emit(v1) v2 = ir.Const(7) v3 = ir.Add(v1, v2, "add", ir.i32) - self.b.emit(v3) + # self.b.emit(v3) self.b.emit(ir.Jump(f.epiloog)) self.cf.run(self.m) diff -r 5d03c10fe19d -r c0d9837acde8 test/testsamples.py --- a/test/testsamples.py Thu May 29 10:47:28 2014 +0200 +++ b/test/testsamples.py Thu May 29 12:13:37 2014 +0200 @@ -5,36 +5,60 @@ from testzcc import relpath from ppci.buildfunctions import assemble, c3compile, link -startercode = """ -section reset -mov sp, 0x30000 ; setup stack pointer -BL sample_start ; Branch to sample start -local_loop: -B local_loop +mod_io_src = """ +module io; +import arch; + +function void println(string txt) +{ + print(txt); + arch.putc(10); // Newline! +} + +function void print(string txt) +{ + var int i; + i = 0; + + while (i < txt->len) + { + arch.putc(cast(txt->txt[i])); + i = i + 1; + } +} + +// Print integer in hexadecimal notation: +function void print_int(int i) +{ + print("0x"); + + // int txt[20]; + var int b; + var int c; + + for (b=28; b >= 0; b = b - 4) + { + c = (i >> b) & 0xF; + if (c < 10) + { + arch.putc( 48 + c ); + } + else + { + arch.putc( 65 - 10 + c ); + } + } + + arch.putc(10); // Newline! +} + +function void print2(string label, int value) +{ + print(label); + print_int(value); +} """ -modarchcode = """ -module arch; - -function void putc(int c) -{ - var int *UART0DR; - UART0DR = cast(0x10009000); // UART0 DR register - *UART0DR = c; -} - -""" - -arch_mmap = """ -MEMORY image LOCATION=0x10000 SIZE=0x10000 { - SECTION(reset) - SECTION(code) -} - -MEMORY ram LOCATION=0x20000 SIZE=0x10000 { - SECTION(data) -} -""" class Samples: def testPrint(self): @@ -132,7 +156,7 @@ do5(); } """ - res = "".join("G=0x{0:08X}\n".format(a) for a in [1,2,7,8,13]) + res = "".join("G=0x{0:08X}\n".format(a) for a in [1, 2, 7, 8, 13]) self.do(snippet, res) @@ -142,6 +166,36 @@ self.skipTest('Not running qemu tests') def do(self, src, expected_output): + startercode = """ + section reset + mov sp, 0x30000 ; setup stack pointer + BL sample_start ; Branch to sample start + local_loop: + B local_loop + """ + + modarchcode = """ + module arch; + + function void putc(int c) + { + var int *UART0DR; + UART0DR = cast(0x10009000); // UART0 DR register + *UART0DR = c; + } + + """ + + arch_mmap = """ + MEMORY image LOCATION=0x10000 SIZE=0x10000 { + SECTION(reset) + SECTION(code) + } + + MEMORY ram LOCATION=0x20000 SIZE=0x10000 { + SECTION(data) + } + """ # Construct binary file from snippet: o1 = assemble(io.StringIO(startercode), 'arm') o2 = c3compile([ @@ -164,6 +218,34 @@ self.assertEqual(expected_output, res) +class TestSamplesOnX86(unittest.TestCase, Samples): + def setUp(self): + if not has_qemu(): + self.skipTest('Not running qemu tests') + self.skipTest('No x86 target yet') + + def do(self, src, expected_output): + # Construct binary file from snippet: + o1 = assemble(io.StringIO(startercode), 'x86') + o2 = c3compile([ + relpath('..', 'kernel', 'src', 'io.c3'), + io.StringIO(modarchcode), + io.StringIO(src)], [], 'x86') + o3 = link([o2, o1], io.StringIO(arch_mmap), 'x86') + + img_data = o3.get_image('image') + sample_filename = 'testsample.bin' + with open(sample_filename, 'wb') as f: + f.write(img_data) + + # Check bin file exists: + self.assertTrue(os.path.isfile(sample_filename)) + + # Run bin file in emulator: + res = runQemu(sample_filename, machine='vexpress-a9') + os.remove(sample_filename) + self.assertEqual(expected_output, res) + # TODO: test samples on thumb target.. diff -r 5d03c10fe19d -r c0d9837acde8 test/testx86asm.py --- a/test/testx86asm.py Thu May 29 10:47:28 2014 +0200 +++ b/test/testx86asm.py Thu May 29 12:13:37 2014 +0200 @@ -1,40 +1,46 @@ #!/usr/bin/python import unittest +from ppci.target.target_list import x86target from testasm import AsmTestCaseBase +from ppci.outstream import BinaryOutputStream +from ppci.objectfile import ObjectFile class AssemblerTestCase(AsmTestCaseBase): - """ - test methods start with 'test*' + """ + test methods start with 'test*' Checks several assembly constructs agains their bytecodes """ def setUp(self): - self.skipTest('not implemented yet') - self.assembler = Assembler('x86-64') - a = Assembler() + self.target = x86target + self.obj = ObjectFile() + self.ostream = BinaryOutputStream(self.obj) + self.ostream.select_section('code') + self.assembler = self.target.assembler + self.assembler.prepare() - @unittest.skip def testX86(self): - self.feed('mov rax, rbx') # ; 0x48, 0x89, 0xd8 - self.feed('xor rcx, rbx') # ; 0x48, 0x31, 0xd9 - self.feed('inc rcx') # ; 0x48 0xff 0xc1 + self.feed('mov rax, rbx') + self.feed('xor rcx, rbx') + self.feed('inc rcx') self.check('48 89 d8 48 31 d9 48 ff c1') - - def tstAssembler(self): - """ Check all kind of assembler cases """ - assert(assembler.shortjump(5) == [0xeb, 0x5]) - assert(assembler.shortjump(-2) == [0xeb, 0xfc]) - assert(assembler.shortjump(10,'GE') == [0x7d, 0xa]) - assert(assembler.nearjump(5) == [0xe9, 0x5,0x0,0x0,0x0]) - assert(assembler.nearjump(-2) == [0xe9, 0xf9, 0xff,0xff,0xff]) - assert(assembler.nearjump(10,'LE') == [0x0f, 0x8e, 0xa,0x0,0x0,0x0]) + @unittest.skip('not implemented') + def testJumpingAround(self): + """ Check all kind of assembler cases """ + assert(assembler.shortjump(5) == [0xeb, 0x5]) + assert(assembler.shortjump(-2) == [0xeb, 0xfc]) + assert(assembler.shortjump(10,'GE') == [0x7d, 0xa]) + assert(assembler.nearjump(5) == [0xe9, 0x5,0x0,0x0,0x0]) + assert(assembler.nearjump(-2) == [0xe9, 0xf9, 0xff,0xff,0xff]) + assert(assembler.nearjump(10,'LE') == [0x0f, 0x8e, 0xa,0x0,0x0,0x0]) + + @unittest.skip('not implemented') def testCall(self): self.feed('call r10') self.check('') self.feed('call rcx') - # assert(assembler.call('r10') == [0x41, 0xff, 0xd2]) # assert(assembler.call('rcx') == [0xff, 0xd1]) @@ -61,12 +67,14 @@ self.feed('pop r12') self.check('5b 5d 41 5c') + @unittest.skip('not implemented') def testAsmLoads(self): - # TODO constant add testcases - assert(assembler.mov('rbx', 'r14') == [0x4c, 0x89, 0xf3]) - assert(assembler.mov('r12', 'r8') == [0x4d, 0x89, 0xc4]) - assert(assembler.mov('rdi', 'rsp') == [0x48, 0x89, 0xe7]) + self.feed('mov rbx, r14') + self.feed('mov r12, r8') + self.feed('mov rdi, rsp') + self.check('4c 89 f3 4d 89 c4 48 89 e7') + @unittest.skip('not implemented') def testAsmMemLoads(self): assert(assembler.mov('rax', ['r8','r15',0x11]) == [0x4b,0x8b,0x44,0x38,0x11]) assert(assembler.mov('r13', ['rbp','rcx',0x23]) == [0x4c,0x8b,0x6c,0xd,0x23]) @@ -79,6 +87,7 @@ assert(assembler.mov('r11', ['RIP', 0xf]) == [0x4c,0x8b,0x1d,0x0f,0x0,0x0,0x0]) + @unittest.skip def testAsmMemStores(self): assert(assembler.mov(['rbp', 0x13],'rbx') == [0x48,0x89,0x5d,0x13]) assert(assembler.mov(['r12', 0x12],'r9') == [0x4d,0x89,0x4c,0x24,0x12]) @@ -90,6 +99,7 @@ assert(assembler.mov(['RIP', 0xf], 'r9') == [0x4c,0x89,0x0d,0x0f,0x0,0x0,0x0]) + @unittest.skip def testAsmMOV8(self): assert(assembler.mov(['rbp', -8], 'al') == [0x88, 0x45, 0xf8]) assert(assembler.mov(['r11', 9], 'cl') == [0x41, 0x88, 0x4b, 0x09]) @@ -97,12 +107,14 @@ assert(assembler.mov(['rbx'], 'al') == [0x88, 0x03]) assert(assembler.mov(['r11'], 'dl') == [0x41, 0x88, 0x13]) + @unittest.skip def testAsmLea(self): assert(assembler.leareg64('r11', ['RIP', 0xf]) == [0x4c,0x8d,0x1d,0x0f,0x0,0x0,0x0]) assert(assembler.leareg64('rsi', ['RIP', 0x7]) == [0x48,0x8d,0x35,0x07,0x0,0x0,0x0]) assert(assembler.leareg64('rcx', ['rbp', -8]) == [0x48,0x8d,0x4d,0xf8]) + @unittest.skip def testAssemblerCMP(self): assert(assembler.cmpreg64('rdi', 'r13') == [0x4c, 0x39, 0xef]) assert(assembler.cmpreg64('rbx', 'r14') == [0x4c, 0x39, 0xf3]) @@ -111,6 +123,7 @@ assert(assembler.cmpreg64('rdi', 1) == [0x48, 0x83, 0xff, 0x01]) assert(assembler.cmpreg64('r11', 2) == [0x49, 0x83, 0xfb, 0x02]) + @unittest.skip def testAssemblerADD(self): assert(assembler.addreg64('rbx', 'r13') == [0x4c, 0x01, 0xeb]) assert(assembler.addreg64('rax', 'rbx') == [0x48, 0x01, 0xd8]) @@ -120,6 +133,7 @@ assert(assembler.addreg64('r11', 0x1234567) == [0x49, 0x81, 0xc3, 0x67, 0x45,0x23,0x1]) assert(assembler.addreg64('rsp', 0x33) == [0x48, 0x83, 0xc4, 0x33]) + @unittest.skip def testAssemblerSUB(self): assert(assembler.subreg64('rdx', 'r14') == [0x4c, 0x29, 0xf2]) assert(assembler.subreg64('r15', 'rbx') == [0x49, 0x29, 0xdf]) @@ -128,11 +142,13 @@ assert(assembler.subreg64('rsp', 0x123456) == [0x48, 0x81, 0xec, 0x56,0x34,0x12,0x0]) assert(assembler.subreg64('rsp', 0x12) == [0x48, 0x83, 0xec, 0x12]) + @unittest.skip def testAssemblerIDIV(self): assert(assembler.idivreg64('r11') == [0x49, 0xf7, 0xfb]) assert(assembler.idivreg64('rcx') == [0x48, 0xf7, 0xf9]) assert(assembler.idivreg64('rsp') == [0x48, 0xf7, 0xfc]) + @unittest.skip def testAssemblerIMUL(self): assert(assembler.imulreg64_rax('rdi') == [0x48, 0xf7, 0xef]) assert(assembler.imulreg64_rax('r10') == [0x49, 0xf7, 0xea]) @@ -147,4 +163,3 @@ if __name__ == '__main__': unittest.main() - diff -r 5d03c10fe19d -r c0d9837acde8 user/build.xml --- a/user/build.xml Thu May 29 10:47:28 2014 +0200 +++ b/user/build.xml Thu May 29 12:13:37 2014 +0200 @@ -22,11 +22,16 @@ includes="lib.c3;ipc.c3" target="arm" output="init.o"/> + + objects="init.o;lib.o" /> + +