From: George Koehler Date: Mon, 6 Feb 2017 19:17:28 +0000 (-0500) Subject: Change RELOPPC to allow gap between ha16 and lo16. X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=530860d7bb088cc51e8f0bcdc913af5190532fae;p=ack.git Change RELOPPC to allow gap between ha16 and lo16. This change breaks old .o files. The linker will probably (but perhaps not always) say, "Don't know how to read from PowerPC fixup", when it encounters a RELOPPC in the old format. The new format divides the 32-bit value into two parts. The lower 26 bits encode a signed 26-bit offset from the symbol. The higher 6 bits encode the distance (in 1 to 63 instructions) from the hi16 or ha16 to the lo16. The old format had a 32-bit offset, but the lo16 needed to be in the next instruction after the hi16 or ha16. The assembler fails if an offset is too big for signed 26-bit. Before this change, we restricted branch instructions to a signed 26-bit offset, but now I also restrict hi16 and ha16 instructions. --- diff --git a/mach/powerpc/as/mach5.c b/mach/powerpc/as/mach5.c index 446c8019c..e54adb858 100644 --- a/mach/powerpc/as/mach5.c +++ b/mach/powerpc/as/mach5.c @@ -1,8 +1,8 @@ /* - * We use RELOPPC to relocate hi16[expr] or ha16[expr]. This requires - * a matching lo16[expr] in the next instruction, because RELOPPC - * handles a hi16/lo16 or ha16/lo16 pair. RELOPPC only works with - * certain specific opcodes. + * To relocate hi16[expr] or ha16[expr], we must find a matching + * lo16[expr] in a later instruction. This is because RELOPPC needs a + * hi16/lo16 or ha16/lo16 pair. Also, RELOPPC only works with certain + * specific opcodes. */ static item_t *last_und_item; @@ -53,6 +53,7 @@ void no_hl(void) { word_t eval_hl(expr_t* expr, int token) { struct hirel* hr; + ADDR_T distance; word_t val = expr->val; uint16_t hi = val >> 16; uint16_t lo = val & 0xffff; @@ -64,6 +65,30 @@ word_t eval_hl(expr_t* expr, int token) else hl_item = NULL; + if (pass == PASS_3) { + hr = hr_head; + if (hr && hr->hr_hidot == DOTVAL && hr->hr_dottyp == DOTTYP) { + /* + * We have a hirel from emit_hl() PASS_2. Encode distance + * to the matching lo16 in top 6 bits of hi. + */ + distance = hr->hr_lodot - DOTVAL; + if (distance & 0x0003) + serror("matching lo16 is misaligned"); + if (distance & ~0x00fc) + serror("matching lo16 is too far away"); + fit(fitx((int16_t)hi, 10)); + hi = (distance << 8) | (hi & 0x03ff); + + /* Remove hirel from list. */ + hr_head = hr->hr_next; + free(hr); + + /* No sign adjustment; linker will do it later. */ + return hi; + } + } + switch (token) { case OP_HA: /* ha16[expr] */ /* @@ -75,19 +100,6 @@ word_t eval_hl(expr_t* expr, int token) hi++; /* FALLTHROUGH */ case OP_HI: /* hi16[expr] */ - if (pass == PASS_3) { - /* Get the hirel from emit_hl() PASS_2. */ - hr = hr_head; - if (hr && hr->hr_hidot == DOTVAL && hr->hr_dottyp == DOTTYP) { - /* For now, the lo16 must be in the next instruction. */ - if (DOTVAL + 4 != hr->hr_lodot) - serror("%s needs lo16 in next instruction", - token == OP_HI ? "hi16" : "ha16"); - - hr_head = hr->hr_next; - free(hr); - } - } return hi; case OP_LO: /* lo16[expr] */ return lo; @@ -176,6 +188,7 @@ void emit_hl(word_t in) if (hr_match(hr, und)) { /* Found hr, remove it from list. */ *hrlink = hr->hr_inext; + hr->hr_inext = NULL; break; } hrlink = &(hr->hr_inext); diff --git a/util/led/relocate.c b/util/led/relocate.c index 036b7dbb8..9455b64b5 100644 --- a/util/led/relocate.c +++ b/util/led/relocate.c @@ -129,44 +129,46 @@ static bool is_powerpc_memory_op(uint32_t opcode) return false; } -/* PowerPC fixups are complex as we need to patch up to the next two - * instructions in one of several different ways, depending on what the - * instructions area. +static bool is_powerpc_ori(uint32_t opcode) +{ + return (opcode & 0xfc000000) == 0x60000000; +} + +/* PowerPC fixups are complex as we need to patch one or two + * instructions in one of several different ways. */ static uint32_t get_powerpc_valu(char* addr, uint16_t type) { - uint32_t opcode1 = read4(addr+0, type); - uint32_t opcode2 = read4(addr+4, type); + uint32_t opcode1 = read4(addr, type); + uint32_t opcode2 = 0; if ((opcode1 & 0xfc000000) == 0x48000000) { /* branch instruction */ return opcode1 & 0x03fffffd; } - else if (((opcode1 & 0xfc1f0000) == 0x3c000000) && - ((opcode2 & 0xfc000000) == 0x60000000)) + else if ((opcode1 & 0xfc1f0000) == 0x3c000000) { - /* addis / ori instruction pair */ - return ((opcode1 & 0xffff) << 16) | (opcode2 & 0xffff); - } - else if (((opcode1 & 0xfc1f0000) == 0x3c000000) && - is_powerpc_memory_op(opcode2)) - { - /* addis / memoryop instruction pair */ - uint16_t hi = opcode1 & 0xffff; - uint16_t lo = opcode2 & 0xffff; + /* addis, paired with a later instruction */ + uint16_t distance, hi, lo; - /* Undo the sign adjustment (see mach/powerpc/as/mach5.c). */ + distance = (opcode1 & 0xfc00) >> 8; + /* XXX addr + distance might be too big */ + if (distance) + opcode2 = read4(addr + distance, type); - if (lo > 0x7fff) - hi--; + hi = opcode1 & 0x03ff; + lo = opcode2 & 0xffff; + if (hi & 0x0200) + hi |= 0xfc00; /* sign extension */ - return ((hi << 16) | lo); + if (is_powerpc_memory_op(opcode2) || is_powerpc_ori(opcode2)) + return (hi << 16) | lo; } - fatal("Don't know how to read from PowerPC fixup on instructions 0x%08x+0x%08x", - opcode1, opcode2); + fatal("Don't know how to read from PowerPC fixup on instructions 0x%08lx+0x%08lx", + (unsigned long)opcode1, (unsigned long)opcode2); } /* @@ -283,15 +285,14 @@ static void put_vc4_valu(char* addr, uint32_t value) assert(0 && "unrecognised VC4 instruction"); } -/* PowerPC fixups are complex as we need to patch up to the next two - * instructions in one of several different ways, depending on what the - * instructions area. +/* PowerPC fixups are complex as we need to patch one or two + * instructions in one of several different ways. */ static void put_powerpc_valu(char* addr, uint32_t value, uint16_t type) { - uint32_t opcode1 = read4(addr+0, type); - uint32_t opcode2 = read4(addr+4, type); + uint32_t opcode1 = read4(addr, type); + uint32_t opcode2 = 0; if ((opcode1 & 0xfc000000) == 0x48000000) { @@ -299,36 +300,38 @@ static void put_powerpc_valu(char* addr, uint32_t value, uint16_t type) uint32_t i = opcode1 & ~0x03fffffd; i |= value & 0x03fffffd; write4(i, addr, type); + return; } - else if (((opcode1 & 0xfc1f0000) == 0x3c000000) && - ((opcode2 & 0xfc000000) == 0x60000000)) + else if ((opcode1 & 0xfc1f0000) == 0x3c000000) { - /* addis / ori instruction pair */ - uint16_t hi = value >> 16; - uint16_t lo = value & 0xffff; + /* addis, paired with a later instruction */ + uint16_t distance, hi, lo; + bool adj; - write4((opcode1 & 0xffff0000) | hi, addr+0, type); - write4((opcode2 & 0xffff0000) | lo, addr+4, type); - } - else if (((opcode1 & 0xfc1f0000) == 0x3c000000) && - is_powerpc_memory_op(opcode2)) - { - /* addis / memoryop instruction pair */ - uint16_t hi = value >> 16; - uint16_t lo = value & 0xffff; + distance = (opcode1 & 0xfc00) >> 8; + /* XXX addr + distance might be too big */ + if (distance) + opcode2 = read4(addr + distance, type); - /* Apply the sign adjustment (see mach/powerpc/as/mach5.c). */ + hi = value >> 16; + lo = value & 0xffff; - if (lo > 0x7fff) + /* Apply the sign adjustment (see mach/powerpc/as/mach5.c). */ + adj = is_powerpc_memory_op(opcode2); + if (adj && lo > 0x7fff) hi++; - write4((opcode1 & 0xffff0000) | hi, addr+0, type); - write4((opcode2 & 0xffff0000) | lo, addr+4, type); + if (adj || is_powerpc_ori(opcode2)) { + opcode1 = (opcode1 & 0xffff0000) | hi; + opcode2 = (opcode2 & 0xffff0000) | lo; + write4(opcode1, addr, type); + write4(opcode2, addr + distance, type); + return; + } } - else - fatal("Don't know how to write a PowerPC fixup to instructions 0x%08x+0x%08x", - opcode1, opcode2); + fatal("Don't know how to write a PowerPC fixup to instructions 0x%08lx+0x%08lx", + (unsigned long)opcode1, (unsigned long)opcode2); } /*