1987-03-09 19:15:41 +00:00
|
|
|
/*
|
|
|
|
* (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
|
|
|
|
* See the copyright notice in the ACK home directory, in the file "Copyright".
|
|
|
|
*/
|
1985-01-10 13:35:39 +00:00
|
|
|
#ifndef lint
|
1994-06-24 11:31:16 +00:00
|
|
|
static char rcsid[] = "$Id$";
|
1985-01-10 13:35:39 +00:00
|
|
|
#endif
|
|
|
|
|
2006-07-30 23:40:35 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
2013-05-21 22:17:30 +00:00
|
|
|
#include <stdint.h>
|
2017-01-15 10:51:37 +00:00
|
|
|
#include <stdbool.h>
|
2013-05-17 21:40:50 +00:00
|
|
|
#include <assert.h>
|
2006-07-30 23:40:35 +00:00
|
|
|
#include "out.h"
|
1985-01-10 13:35:39 +00:00
|
|
|
#include "const.h"
|
|
|
|
#include "debug.h"
|
|
|
|
#include "defs.h"
|
|
|
|
#include "orig.h"
|
2018-03-11 11:37:23 +00:00
|
|
|
#include "sym.h"
|
2019-03-24 09:08:45 +00:00
|
|
|
#include "relocate.h"
|
1985-01-10 13:35:39 +00:00
|
|
|
|
2018-09-14 07:28:35 +00:00
|
|
|
#define UBYTE(x) ((x)&BYTEMASK)
|
1985-01-10 13:35:39 +00:00
|
|
|
|
2016-09-17 10:43:15 +00:00
|
|
|
static uint16_t read2(char* addr, int type)
|
2013-05-07 23:48:48 +00:00
|
|
|
{
|
|
|
|
unsigned short word0, word1;
|
|
|
|
|
|
|
|
if (type & RELBR)
|
|
|
|
return (UBYTE(addr[0]) << WIDTH) + UBYTE(addr[1]);
|
|
|
|
else
|
|
|
|
return (UBYTE(addr[1]) << WIDTH) + UBYTE(addr[0]);
|
|
|
|
}
|
|
|
|
|
2016-09-17 10:43:15 +00:00
|
|
|
static uint32_t read4(char* addr, int type)
|
2013-05-07 23:48:48 +00:00
|
|
|
{
|
|
|
|
unsigned short word0, word1;
|
|
|
|
|
2018-09-14 07:28:35 +00:00
|
|
|
if (type & RELBR)
|
|
|
|
{
|
2013-05-07 23:48:48 +00:00
|
|
|
word0 = (UBYTE(addr[0]) << WIDTH) + UBYTE(addr[1]);
|
|
|
|
word1 = (UBYTE(addr[2]) << WIDTH) + UBYTE(addr[3]);
|
2018-09-14 07:28:35 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-05-07 23:48:48 +00:00
|
|
|
word0 = (UBYTE(addr[1]) << WIDTH) + UBYTE(addr[0]);
|
|
|
|
word1 = (UBYTE(addr[3]) << WIDTH) + UBYTE(addr[2]);
|
|
|
|
}
|
|
|
|
if (type & RELWR)
|
|
|
|
return ((long)word0 << (2 * WIDTH)) + word1;
|
|
|
|
else
|
|
|
|
return ((long)word1 << (2 * WIDTH)) + word0;
|
|
|
|
}
|
|
|
|
|
2013-05-21 22:17:30 +00:00
|
|
|
/* VideoCore 4 fixups are complex as we need to patch the instruction in
|
|
|
|
* one of several different ways (depending on what the instruction is).
|
|
|
|
*/
|
|
|
|
|
2016-09-17 10:43:15 +00:00
|
|
|
static uint32_t get_vc4_valu(char* addr)
|
2013-05-21 22:17:30 +00:00
|
|
|
{
|
|
|
|
uint16_t opcode = read2(addr, 0);
|
|
|
|
|
|
|
|
if ((opcode & 0xff00) == 0xe700)
|
|
|
|
{
|
|
|
|
/* ld<w> rd, $+o: [1110 0111 ww 0 d:5] [11111 o:27]
|
|
|
|
* st<w> rd, $+o: [1110 0111 ww 1 d:5] [11111 o:27]
|
|
|
|
*/
|
|
|
|
|
2018-09-14 07:28:35 +00:00
|
|
|
int32_t value = read4(addr + 2, 0);
|
2013-05-21 22:17:30 +00:00
|
|
|
value &= 0x07ffffff;
|
2018-09-14 07:28:35 +00:00
|
|
|
value = value << 5 >> 5;
|
2013-05-21 22:17:30 +00:00
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((opcode & 0xf080) == 0x9000)
|
|
|
|
{
|
|
|
|
/* b<cc> $+o*2: [1001 cccc 0ooo oooo] [oooo oooo oooo oooo]
|
|
|
|
* Yes, big-endian (the first 16 bits is the MSB).
|
|
|
|
*/
|
|
|
|
|
|
|
|
uint32_t value = read4(addr, RELWR);
|
|
|
|
value &= 0x007fffff;
|
2018-09-14 07:28:35 +00:00
|
|
|
value = value << 9 >> 9;
|
2013-05-21 22:17:30 +00:00
|
|
|
value *= 2;
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((opcode & 0xf080) == 0x9080)
|
|
|
|
{
|
|
|
|
/* bl $+o*2: [1001 oooo 1ooo oooo] [oooo oooo oooo oooo]
|
|
|
|
* Yes, big-endian (the first 16 bits is the MSB).
|
|
|
|
* (Note that o is split.)
|
|
|
|
*/
|
|
|
|
|
|
|
|
int32_t value = read4(addr, RELWR);
|
|
|
|
int32_t lov = value & 0x007fffff;
|
|
|
|
int32_t hiv = value & 0x0f000000;
|
2018-09-14 07:28:35 +00:00
|
|
|
value = lov | (hiv >> 1);
|
|
|
|
value = value << 5 >> 5;
|
2013-05-21 22:17:30 +00:00
|
|
|
value *= 2;
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((opcode & 0xffe0) == 0xe500)
|
|
|
|
{
|
2018-09-14 07:28:35 +00:00
|
|
|
/* lea: [1110 0101 000 d:5] [o:32] */
|
2013-05-21 22:17:30 +00:00
|
|
|
|
2018-09-14 07:28:35 +00:00
|
|
|
return read4(addr + 2, 0);
|
|
|
|
}
|
2013-05-21 22:17:30 +00:00
|
|
|
|
|
|
|
assert(0 && "unrecognised VC4 instruction");
|
|
|
|
}
|
|
|
|
|
2017-01-15 10:51:37 +00:00
|
|
|
static bool is_powerpc_memory_op(uint32_t opcode)
|
|
|
|
{
|
2017-01-23 21:19:38 +00:00
|
|
|
/* Tests for any PowerPC memory indirection instruction (or
|
|
|
|
* addi) where the payload is a *signed* 16-bit value. */
|
2017-01-15 10:51:37 +00:00
|
|
|
switch ((opcode & 0xfc000000) >> 26)
|
|
|
|
{
|
2017-01-23 21:19:38 +00:00
|
|
|
case 14: /* addi */
|
2017-01-15 10:51:37 +00:00
|
|
|
case 34: /* lbz */
|
2017-01-23 21:19:38 +00:00
|
|
|
case 48: /* lfs */
|
|
|
|
case 50: /* lfd */
|
|
|
|
case 42: /* lha */
|
2017-01-15 10:51:37 +00:00
|
|
|
case 40: /* lhz */
|
|
|
|
case 32: /* lwz */
|
|
|
|
case 38: /* stb */
|
2017-01-23 21:19:38 +00:00
|
|
|
case 52: /* stfs */
|
|
|
|
case 54: /* stfd */
|
2017-01-15 10:51:37 +00:00
|
|
|
case 44: /* sth */
|
|
|
|
case 36: /* stw */
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-09-17 10:43:15 +00:00
|
|
|
/* 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 uint32_t get_powerpc_valu(char* addr, uint16_t type)
|
|
|
|
{
|
2018-09-14 07:28:35 +00:00
|
|
|
uint32_t opcode1 = read4(addr + 0, type);
|
|
|
|
uint32_t opcode2 = read4(addr + 4, type);
|
2016-09-17 10:43:15 +00:00
|
|
|
|
|
|
|
if ((opcode1 & 0xfc000000) == 0x48000000)
|
|
|
|
{
|
|
|
|
/* branch instruction */
|
|
|
|
return opcode1 & 0x03fffffd;
|
|
|
|
}
|
2018-09-14 07:28:35 +00:00
|
|
|
else if (((opcode1 & 0xfc1f0000) == 0x3c000000) && ((opcode2 & 0xfc000000) == 0x60000000))
|
2016-09-17 10:43:15 +00:00
|
|
|
{
|
2017-01-15 09:31:20 +00:00
|
|
|
/* addis / ori instruction pair */
|
2016-09-17 10:43:15 +00:00
|
|
|
return ((opcode1 & 0xffff) << 16) | (opcode2 & 0xffff);
|
|
|
|
}
|
2018-09-14 07:28:35 +00:00
|
|
|
else if (((opcode1 & 0xfc1f0000) == 0x3c000000) && is_powerpc_memory_op(opcode2))
|
2017-01-15 10:51:37 +00:00
|
|
|
{
|
|
|
|
/* addis / memoryop instruction pair */
|
|
|
|
uint16_t hi = opcode1 & 0xffff;
|
|
|
|
uint16_t lo = opcode2 & 0xffff;
|
|
|
|
|
|
|
|
/* Undo the sign adjustment (see mach/powerpc/as/mach5.c). */
|
|
|
|
|
|
|
|
if (lo > 0x7fff)
|
|
|
|
hi--;
|
|
|
|
|
|
|
|
return ((hi << 16) | lo);
|
|
|
|
}
|
2017-01-15 09:31:20 +00:00
|
|
|
|
2018-09-14 07:28:35 +00:00
|
|
|
fatal(
|
|
|
|
"Don't know how to read from PowerPC fixup on instructions 0x%08lx+0x%08lx",
|
|
|
|
(unsigned long)opcode1, (unsigned long)opcode2);
|
Add RELOLIS for PowerPC lis with ha16 or hi16.
The new relocation type RELOLIS handles these instructions:
lis RT, ha16[expr] == addis RT, r0, ha16[expr]
lis RT, hi16[expr] == addis RT, r0, hi16[expr]
RELOLIS stores a 32-bit value in the program text. In this value, the
high bit is a ha16 flag, the next 5 bits are the target register RT,
and the low bits are a signed 26-bit offset. The linker replaces this
value with the lis instruction.
The old RELOPPC relocated a ha16/lo16 or hi16/lo16 pair. The new
RELOLIS relocates only a ha16 or hi16, so it is no longer necessary to
have a matching lo16 in the next instruction. The disadvantage is
that RELOLIS has only a signed 26-bit offset, not a 32-bit offset.
Switch the assembler to use RELOLIS for ha16 or hi16 and RELO2 for
lo16. The li32 instruction still uses the old RELOPPC relocation.
This is not the same as my RELOPPC change from my recent mail to
tack-devel (https://sourceforge.net/p/tack/mailman/message/35651528/).
This commit is on a different branch. Here I am throwing away my
RELOPPC change and instead trying RELOLIS.
2017-02-08 16:46:31 +00:00
|
|
|
}
|
|
|
|
|
2017-10-18 19:39:31 +00:00
|
|
|
/* RELOPPC_LIS stores a signed 26-bit offset in the low bits. */
|
2018-09-14 07:28:35 +00:00
|
|
|
static uint32_t get_lis_valu(char* addr, uint16_t type)
|
Add RELOLIS for PowerPC lis with ha16 or hi16.
The new relocation type RELOLIS handles these instructions:
lis RT, ha16[expr] == addis RT, r0, ha16[expr]
lis RT, hi16[expr] == addis RT, r0, hi16[expr]
RELOLIS stores a 32-bit value in the program text. In this value, the
high bit is a ha16 flag, the next 5 bits are the target register RT,
and the low bits are a signed 26-bit offset. The linker replaces this
value with the lis instruction.
The old RELOPPC relocated a ha16/lo16 or hi16/lo16 pair. The new
RELOLIS relocates only a ha16 or hi16, so it is no longer necessary to
have a matching lo16 in the next instruction. The disadvantage is
that RELOLIS has only a signed 26-bit offset, not a 32-bit offset.
Switch the assembler to use RELOLIS for ha16 or hi16 and RELO2 for
lo16. The li32 instruction still uses the old RELOPPC relocation.
This is not the same as my RELOPPC change from my recent mail to
tack-devel (https://sourceforge.net/p/tack/mailman/message/35651528/).
This commit is on a different branch. Here I am throwing away my
RELOPPC change and instead trying RELOLIS.
2017-02-08 16:46:31 +00:00
|
|
|
{
|
|
|
|
uint32_t valu = read4(addr, type) & 0x03ffffff;
|
|
|
|
if (valu & 0x02000000)
|
|
|
|
valu |= 0xfc000000; /* sign extension */
|
|
|
|
return valu;
|
2016-09-17 10:43:15 +00:00
|
|
|
}
|
|
|
|
|
2018-09-09 10:23:59 +00:00
|
|
|
/* RELOMIPS is used for j and b instructions only. */
|
|
|
|
static uint32_t get_mips_valu(char* addr)
|
|
|
|
{
|
|
|
|
uint32_t value = read4(addr, 0);
|
|
|
|
switch (value >> 26)
|
|
|
|
{
|
|
|
|
case 2: /* j */
|
|
|
|
case 3: /* jal */
|
|
|
|
case 29: /* jalx */
|
|
|
|
/* Unsigned 26-bit payload. */
|
2018-09-14 07:28:35 +00:00
|
|
|
value = value & ((1 << 26) - 1);
|
2018-09-09 10:23:59 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default: /* assume everything else is a b, there are lots */
|
|
|
|
/* Signed 16-bit payload. */
|
|
|
|
value = ((int32_t)value << 16) >> 16;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The value has two implicit zero bits on the bottom. */
|
|
|
|
value <<= 2;
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
1985-01-10 13:35:39 +00:00
|
|
|
/*
|
|
|
|
* The bits in type indicate how many bytes the value occupies and what
|
|
|
|
* significance should be attributed to each byte.
|
|
|
|
*/
|
2016-09-17 10:43:15 +00:00
|
|
|
static uint32_t getvalu(char* addr, uint16_t type)
|
1985-01-10 13:35:39 +00:00
|
|
|
{
|
2018-09-14 07:28:35 +00:00
|
|
|
switch (type & RELSZ)
|
|
|
|
{
|
|
|
|
case RELO1:
|
|
|
|
return UBYTE(addr[0]);
|
|
|
|
case RELO2:
|
|
|
|
case RELO2HI:
|
|
|
|
case RELO2HISAD:
|
|
|
|
return read2(addr, type);
|
|
|
|
case RELO4:
|
|
|
|
return read4(addr, type);
|
|
|
|
case RELOPPC:
|
|
|
|
return get_powerpc_valu(addr, type);
|
|
|
|
case RELOPPC_LIS:
|
|
|
|
return get_lis_valu(addr, type);
|
|
|
|
case RELOVC4:
|
|
|
|
return get_vc4_valu(addr);
|
|
|
|
case RELOMIPS:
|
|
|
|
return get_mips_valu(addr);
|
|
|
|
default:
|
|
|
|
fatal("can't read relocation type %x", type & RELSZ);
|
1985-01-10 13:35:39 +00:00
|
|
|
}
|
|
|
|
/* NOTREACHED */
|
|
|
|
}
|
|
|
|
|
2016-09-17 10:43:15 +00:00
|
|
|
static void write2(uint16_t valu, char* addr, int type)
|
2013-05-07 23:48:48 +00:00
|
|
|
{
|
2018-09-14 07:28:35 +00:00
|
|
|
unsigned short word0, word1;
|
2013-05-07 23:48:48 +00:00
|
|
|
|
2018-09-14 07:28:35 +00:00
|
|
|
if (type & RELBR)
|
|
|
|
{
|
2013-05-07 23:48:48 +00:00
|
|
|
addr[0] = valu >> WIDTH;
|
|
|
|
addr[1] = valu;
|
2018-09-14 07:28:35 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-05-07 23:48:48 +00:00
|
|
|
addr[0] = valu;
|
|
|
|
addr[1] = valu >> WIDTH;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-17 10:43:15 +00:00
|
|
|
static void write4(uint32_t valu, char* addr, int type)
|
2013-05-07 23:48:48 +00:00
|
|
|
{
|
2018-09-14 07:28:35 +00:00
|
|
|
unsigned short word0, word1;
|
2013-05-07 23:48:48 +00:00
|
|
|
|
2018-09-14 07:28:35 +00:00
|
|
|
if (type & RELWR)
|
|
|
|
{
|
2013-05-07 23:48:48 +00:00
|
|
|
word0 = valu >> (2 * WIDTH);
|
|
|
|
word1 = valu;
|
2018-09-14 07:28:35 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-05-07 23:48:48 +00:00
|
|
|
word0 = valu;
|
|
|
|
word1 = valu >> (2 * WIDTH);
|
|
|
|
}
|
2018-09-14 07:28:35 +00:00
|
|
|
if (type & RELBR)
|
|
|
|
{
|
2013-05-07 23:48:48 +00:00
|
|
|
addr[0] = word0 >> WIDTH;
|
|
|
|
addr[1] = word0;
|
|
|
|
addr[2] = word1 >> WIDTH;
|
|
|
|
addr[3] = word1;
|
2018-09-14 07:28:35 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-05-07 23:48:48 +00:00
|
|
|
addr[0] = word0;
|
|
|
|
addr[1] = word0 >> WIDTH;
|
|
|
|
addr[2] = word1;
|
|
|
|
addr[3] = word1 >> WIDTH;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-21 22:17:30 +00:00
|
|
|
/* VideoCore 4 fixups are complex as we need to patch the instruction in
|
|
|
|
* one of several different ways (depending on what the instruction is).
|
|
|
|
*/
|
|
|
|
|
2016-09-17 10:43:15 +00:00
|
|
|
static void put_vc4_valu(char* addr, uint32_t value)
|
2013-05-21 22:17:30 +00:00
|
|
|
{
|
|
|
|
uint16_t opcode = read2(addr, 0);
|
|
|
|
|
|
|
|
if ((opcode & 0xff00) == 0xe700)
|
|
|
|
{
|
|
|
|
/* ld<w> rd, o, (pc): [1110 0111 ww 0 d:5] [11111 o:27]
|
|
|
|
* st<w> rd, o, (pc): [1110 0111 ww 1 d:5] [11111 o:27]
|
|
|
|
*/
|
|
|
|
|
2018-09-14 07:28:35 +00:00
|
|
|
uint32_t v = read4(addr + 2, 0);
|
2013-05-21 22:17:30 +00:00
|
|
|
v &= 0xf8000000;
|
|
|
|
v |= value & 0x07ffffff;
|
2018-09-14 07:28:35 +00:00
|
|
|
write4(v, addr + 2, 0);
|
2013-05-21 22:17:30 +00:00
|
|
|
}
|
|
|
|
else if ((opcode & 0xf080) == 0x9000)
|
|
|
|
{
|
|
|
|
/* b<cc> dest: [1001 cccc 0ooo oooo] [oooo oooo oooo oooo]
|
|
|
|
* Yes, big-endian (the first 16 bits is the MSB).
|
|
|
|
*/
|
|
|
|
|
|
|
|
uint32_t v = read4(addr, RELWR);
|
|
|
|
v &= 0xff800000;
|
2018-09-14 07:28:35 +00:00
|
|
|
v |= (value / 2) & 0x007fffff;
|
2013-05-21 22:17:30 +00:00
|
|
|
write4(v, addr, RELWR);
|
|
|
|
}
|
|
|
|
else if ((opcode & 0xf080) == 0x9080)
|
|
|
|
{
|
|
|
|
/* bl dest: [1001 oooo 1ooo oooo] [oooo oooo oooo oooo]
|
|
|
|
* Yes, big-endian (the first 16 bits is the MSB).
|
|
|
|
* (Note that o is split.)
|
|
|
|
*/
|
|
|
|
|
|
|
|
uint32_t v = read4(addr, RELWR);
|
2018-09-14 07:28:35 +00:00
|
|
|
uint32_t lovalue = (value / 2) & 0x007fffff;
|
|
|
|
uint32_t hivalue = (value / 2) & 0x07800000;
|
2013-05-21 22:17:30 +00:00
|
|
|
v &= 0xf0800000;
|
2018-09-14 07:28:35 +00:00
|
|
|
v |= lovalue | (hivalue << 1);
|
2013-05-21 22:17:30 +00:00
|
|
|
write4(v, addr, RELWR);
|
|
|
|
}
|
|
|
|
else if ((opcode & 0xffe0) == 0xe500)
|
|
|
|
{
|
2018-09-14 07:28:35 +00:00
|
|
|
/* lea: [1110 0101 000 d:5] [o:32] */
|
2013-05-21 22:17:30 +00:00
|
|
|
|
2018-09-14 07:28:35 +00:00
|
|
|
write4(value, addr + 2, 0);
|
|
|
|
}
|
|
|
|
else
|
2013-05-21 22:17:30 +00:00
|
|
|
assert(0 && "unrecognised VC4 instruction");
|
|
|
|
}
|
|
|
|
|
2016-09-17 10:43:15 +00:00
|
|
|
/* 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 void put_powerpc_valu(char* addr, uint32_t value, uint16_t type)
|
|
|
|
{
|
2018-09-14 07:28:35 +00:00
|
|
|
uint32_t opcode1 = read4(addr + 0, type);
|
|
|
|
uint32_t opcode2 = read4(addr + 4, type);
|
2016-09-17 10:43:15 +00:00
|
|
|
|
|
|
|
if ((opcode1 & 0xfc000000) == 0x48000000)
|
|
|
|
{
|
|
|
|
/* branch instruction */
|
|
|
|
uint32_t i = opcode1 & ~0x03fffffd;
|
|
|
|
i |= value & 0x03fffffd;
|
|
|
|
write4(i, addr, type);
|
|
|
|
}
|
2018-09-14 07:28:35 +00:00
|
|
|
else if (((opcode1 & 0xfc1f0000) == 0x3c000000) && ((opcode2 & 0xfc000000) == 0x60000000))
|
2016-09-17 10:43:15 +00:00
|
|
|
{
|
2017-01-15 10:59:33 +00:00
|
|
|
/* addis / ori instruction pair */
|
2016-09-17 10:43:15 +00:00
|
|
|
uint16_t hi = value >> 16;
|
|
|
|
uint16_t lo = value & 0xffff;
|
|
|
|
|
2018-09-14 07:28:35 +00:00
|
|
|
write4((opcode1 & 0xffff0000) | hi, addr + 0, type);
|
|
|
|
write4((opcode2 & 0xffff0000) | lo, addr + 4, type);
|
2016-09-17 10:43:15 +00:00
|
|
|
}
|
2018-09-14 07:28:35 +00:00
|
|
|
else if (((opcode1 & 0xfc1f0000) == 0x3c000000) && is_powerpc_memory_op(opcode2))
|
2017-01-15 10:51:37 +00:00
|
|
|
{
|
|
|
|
/* addis / memoryop instruction pair */
|
|
|
|
uint16_t hi = value >> 16;
|
|
|
|
uint16_t lo = value & 0xffff;
|
|
|
|
|
|
|
|
/* Apply the sign adjustment (see mach/powerpc/as/mach5.c). */
|
|
|
|
|
|
|
|
if (lo > 0x7fff)
|
|
|
|
hi++;
|
|
|
|
|
2018-09-14 07:28:35 +00:00
|
|
|
write4((opcode1 & 0xffff0000) | hi, addr + 0, type);
|
|
|
|
write4((opcode2 & 0xffff0000) | lo, addr + 4, type);
|
2017-01-15 10:51:37 +00:00
|
|
|
}
|
|
|
|
|
2017-01-15 09:31:20 +00:00
|
|
|
else
|
2018-09-14 07:28:35 +00:00
|
|
|
fatal(
|
|
|
|
"Don't know how to write a PowerPC fixup to instructions 0x%08lx+0x%08lx",
|
|
|
|
(unsigned long)opcode1, (unsigned long)opcode2);
|
Add RELOLIS for PowerPC lis with ha16 or hi16.
The new relocation type RELOLIS handles these instructions:
lis RT, ha16[expr] == addis RT, r0, ha16[expr]
lis RT, hi16[expr] == addis RT, r0, hi16[expr]
RELOLIS stores a 32-bit value in the program text. In this value, the
high bit is a ha16 flag, the next 5 bits are the target register RT,
and the low bits are a signed 26-bit offset. The linker replaces this
value with the lis instruction.
The old RELOPPC relocated a ha16/lo16 or hi16/lo16 pair. The new
RELOLIS relocates only a ha16 or hi16, so it is no longer necessary to
have a matching lo16 in the next instruction. The disadvantage is
that RELOLIS has only a signed 26-bit offset, not a 32-bit offset.
Switch the assembler to use RELOLIS for ha16 or hi16 and RELO2 for
lo16. The li32 instruction still uses the old RELOPPC relocation.
This is not the same as my RELOPPC change from my recent mail to
tack-devel (https://sourceforge.net/p/tack/mailman/message/35651528/).
This commit is on a different branch. Here I am throwing away my
RELOPPC change and instead trying RELOLIS.
2017-02-08 16:46:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Writes a PowerPC lis instruction. */
|
|
|
|
static void put_lis_valu(char* addr, uint32_t value, uint16_t type)
|
|
|
|
{
|
|
|
|
uint32_t opcode, reg;
|
|
|
|
uint16_t hi, lo;
|
|
|
|
bool ha16;
|
|
|
|
|
|
|
|
/* ha16 flag in high bit, register in next 5 bits */
|
|
|
|
opcode = read4(addr, type);
|
|
|
|
ha16 = opcode >> 31;
|
|
|
|
reg = (opcode >> 26) & 0x1f;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Apply the sign adjustment if the ha16 flag is set and the
|
|
|
|
* low half is a negative signed 16-bit integer.
|
|
|
|
*/
|
|
|
|
hi = value >> 16;
|
|
|
|
lo = value & 0xffff;
|
|
|
|
if (ha16 && lo > 0x7fff)
|
|
|
|
hi++;
|
|
|
|
|
|
|
|
/* Assemble lis reg, hi == addis reg, r0, hi. */
|
|
|
|
opcode = (15 << 26) | (reg << 21) | (0 << 16) | hi;
|
|
|
|
write4(opcode, addr, type);
|
2016-09-17 10:43:15 +00:00
|
|
|
}
|
|
|
|
|
2018-09-09 10:23:59 +00:00
|
|
|
/* RELOMIPS is used for j and b instructions only. */
|
|
|
|
static void put_mips_valu(char* addr, uint32_t value)
|
|
|
|
{
|
|
|
|
uint32_t opcode = read4(addr, 0);
|
|
|
|
|
|
|
|
/* The two bottom zero bits are implicit. */
|
|
|
|
if (value & 3)
|
Reduce warnings, adjust format strings in util/led
Calls like `debug("something\n", 0, 0, 0, 0)` cause clang warnings,
because debug() is a macro that passes its arguments to printf(), and
clang warns about extra 0s to printf(). Silence the warnings by
hiding the printf() in a new function do_debug(). The code still
passes extra 0s to printf(), but clang can't warn.
Macros debug() and verbose() should use C99 __VA_ARGS__, so they don't
require the extra 0s; but ACK doesn't use __VA_ARGS__ yet.
Adjust some format strings for debug() or fatal(), or cast their
arguments, to match their types. I don't know whether uint32_t is
unsigned int or unsigned long, so I cast it to unsigned long, and
print it with "%lx".
In util/led/sym.c, #include "save.h" to declare savechar(), and use
parentheses to silence a clang warning in hash().
2019-11-01 22:27:34 +00:00
|
|
|
fatal("invalid MIPS relocation value 0x%lx",
|
|
|
|
(unsigned long)value);
|
2018-09-09 10:23:59 +00:00
|
|
|
value >>= 2;
|
|
|
|
|
2018-09-09 12:11:11 +00:00
|
|
|
switch (opcode >> 26)
|
2018-09-09 10:23:59 +00:00
|
|
|
{
|
|
|
|
case 2: /* j */
|
|
|
|
case 3: /* jal */
|
|
|
|
case 29: /* jalx */
|
|
|
|
/* Unsigned 26-bit payload. */
|
2018-09-14 07:28:35 +00:00
|
|
|
value = value & ((1 << 26) - 1);
|
|
|
|
opcode = opcode & ~((1 << 26) - 1);
|
2018-09-09 10:23:59 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default: /* assume everything else is a b, there are lots */
|
|
|
|
/* Signed 16-bit payload. */
|
2018-09-14 07:28:35 +00:00
|
|
|
value = value & ((1 << 16) - 1);
|
|
|
|
opcode = opcode & ~((1 << 16) - 1);
|
2018-09-09 10:23:59 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
write4(opcode | value, addr, 0);
|
|
|
|
}
|
|
|
|
|
1985-01-10 13:35:39 +00:00
|
|
|
/*
|
|
|
|
* The bits in type indicate how many bytes the value occupies and what
|
|
|
|
* significance should be attributed to each byte.
|
|
|
|
* We do not check for overflow.
|
|
|
|
*/
|
2019-03-24 09:08:45 +00:00
|
|
|
static void putvalu(uint32_t valu, char* addr, uint16_t type)
|
1985-01-10 13:35:39 +00:00
|
|
|
{
|
|
|
|
|
2018-09-14 07:28:35 +00:00
|
|
|
switch (type & RELSZ)
|
|
|
|
{
|
|
|
|
case RELO1:
|
|
|
|
addr[0] = valu;
|
|
|
|
break;
|
|
|
|
case RELO2:
|
|
|
|
write2(valu, addr, type);
|
|
|
|
break;
|
|
|
|
case RELO2HI:
|
|
|
|
write2(valu >> 16, addr, type);
|
|
|
|
break;
|
|
|
|
case RELO2HISAD:
|
|
|
|
write2((valu >> 16) + !!(valu & 0x8000), addr, type);
|
|
|
|
break;
|
|
|
|
case RELO4:
|
|
|
|
write4(valu, addr, type);
|
|
|
|
break;
|
|
|
|
case RELOPPC:
|
|
|
|
put_powerpc_valu(addr, valu, type);
|
|
|
|
break;
|
|
|
|
case RELOPPC_LIS:
|
|
|
|
put_lis_valu(addr, valu, type);
|
|
|
|
break;
|
|
|
|
case RELOVC4:
|
|
|
|
put_vc4_valu(addr, valu);
|
|
|
|
break;
|
|
|
|
case RELOMIPS:
|
|
|
|
put_mips_valu(addr, valu);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fatal("can't write relocation type %x", type & RELSZ);
|
1985-01-10 13:35:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-14 07:28:35 +00:00
|
|
|
extern struct outsect outsect[];
|
|
|
|
extern struct orig relorig[];
|
1985-01-10 13:35:39 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* There are two cases: `local' is an undefined external or common name,
|
|
|
|
* or `local' is a section name.
|
|
|
|
* First case: if the name has been defined in another module,
|
|
|
|
* its value is known and can be added. Or_nami will be the
|
|
|
|
* index of the name of the section in which this name was
|
|
|
|
* defined. Otherwise we must change or_nami to the index of
|
|
|
|
* this name in the name table of the output file and leave
|
|
|
|
* its value unchanged.
|
|
|
|
* Second case: we must update the value by the change
|
|
|
|
* in position of the section of local.
|
|
|
|
*/
|
2018-09-14 07:28:35 +00:00
|
|
|
static unsigned addrelo(relo, names, valu_out) struct outrelo* relo;
|
|
|
|
struct outname* names;
|
|
|
|
long* valu_out; /* Out variable. */
|
1985-01-10 13:35:39 +00:00
|
|
|
{
|
2018-09-14 07:28:35 +00:00
|
|
|
register struct outname* local = &names[relo->or_nami];
|
|
|
|
register unsigned short index = NLocals;
|
|
|
|
register long valu = *valu_out;
|
1985-01-10 13:35:39 +00:00
|
|
|
|
2018-09-14 07:28:35 +00:00
|
|
|
if ((local->on_type & S_SCT))
|
|
|
|
{
|
|
|
|
register int sectindex = (local->on_type & S_TYP) - S_MIN;
|
1986-10-20 09:35:51 +00:00
|
|
|
|
1986-10-20 10:17:57 +00:00
|
|
|
valu += relorig[sectindex].org_size;
|
1986-10-20 09:35:51 +00:00
|
|
|
valu += outsect[sectindex].os_base;
|
|
|
|
index += NGlobals + sectindex;
|
2018-09-14 07:28:35 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
register struct outname* name;
|
|
|
|
extern int hash();
|
|
|
|
extern struct outname* searchname();
|
|
|
|
extern unsigned indexof();
|
|
|
|
extern struct outhead outhead;
|
1985-01-10 13:35:39 +00:00
|
|
|
|
|
|
|
name = searchname(local->on_mptr, hash(local->on_mptr));
|
2018-09-14 07:28:35 +00:00
|
|
|
if (name == (struct outname*)0)
|
1985-01-10 13:35:39 +00:00
|
|
|
fatal("name %s not found in pass 2", local->on_mptr);
|
2018-09-14 07:28:35 +00:00
|
|
|
if (ISCOMMON(name) || ISUNDEFINED(name))
|
|
|
|
{
|
|
|
|
debug("can't relocate from %s\n", local->on_mptr, 0, 0, 0);
|
1985-01-10 13:35:39 +00:00
|
|
|
index += indexof(name);
|
2018-09-14 07:28:35 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
1985-01-10 13:35:39 +00:00
|
|
|
valu += name->on_valu;
|
2018-09-14 07:28:35 +00:00
|
|
|
if ((name->on_type & S_TYP) == S_ABS)
|
|
|
|
{
|
1987-08-26 13:22:44 +00:00
|
|
|
index += NGlobals + outhead.oh_nsect;
|
|
|
|
}
|
2018-09-14 07:28:35 +00:00
|
|
|
else
|
|
|
|
index += NGlobals + (name->on_type & S_TYP) - S_MIN;
|
1985-01-10 13:35:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
*valu_out = valu;
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This routine relocates a value in a section pointed to by `emit', of
|
|
|
|
* which the header is pointed to by `head'. Relocation is relative to the
|
|
|
|
* names in `names'; `relo' tells how to relocate.
|
|
|
|
*/
|
2019-03-24 09:08:45 +00:00
|
|
|
void relocate(struct outhead *head, char* emit, struct outname names[], struct outrelo *relo, long off)
|
1985-01-10 13:35:39 +00:00
|
|
|
{
|
2018-09-14 07:28:35 +00:00
|
|
|
long valu;
|
|
|
|
int sectindex = relo->or_sect - S_MIN;
|
|
|
|
extern struct outhead outhead;
|
2018-09-14 09:30:15 +00:00
|
|
|
uint32_t realaddress = outsect[sectindex].os_base + relo->or_addr
|
|
|
|
+ relorig[sectindex].org_size;
|
1985-01-10 13:35:39 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Pick up previous value at location to be relocated.
|
|
|
|
*/
|
1987-05-21 10:06:14 +00:00
|
|
|
valu = getvalu(emit + (relo->or_addr - off), relo->or_type);
|
Reduce warnings, adjust format strings in util/led
Calls like `debug("something\n", 0, 0, 0, 0)` cause clang warnings,
because debug() is a macro that passes its arguments to printf(), and
clang warns about extra 0s to printf(). Silence the warnings by
hiding the printf() in a new function do_debug(). The code still
passes extra 0s to printf(), but clang can't warn.
Macros debug() and verbose() should use C99 __VA_ARGS__, so they don't
require the extra 0s; but ACK doesn't use __VA_ARGS__ yet.
Adjust some format strings for debug() or fatal(), or cast their
arguments, to match their types. I don't know whether uint32_t is
unsigned int or unsigned long, so I cast it to unsigned long, and
print it with "%lx".
In util/led/sym.c, #include "save.h" to declare savechar(), and use
parentheses to silence a clang warning in hash().
2019-11-01 22:27:34 +00:00
|
|
|
debug("read relocation from 0x%08lx type 0x%x value 0x%08lx symbol %u\n",
|
|
|
|
(unsigned long)realaddress, relo->or_type, valu, relo->or_nami);
|
2013-05-07 23:48:48 +00:00
|
|
|
|
1985-01-10 13:35:39 +00:00
|
|
|
/*
|
|
|
|
* Or_nami is an index in the name table of the considered module.
|
|
|
|
* The name of which it is an index can be:
|
|
|
|
* - an undefined external or a common name
|
|
|
|
* - a section name
|
|
|
|
* - the first name outside! the name table (argh)
|
|
|
|
*/
|
2018-09-14 07:28:35 +00:00
|
|
|
if (relo->or_nami < head->oh_nname)
|
|
|
|
{
|
1985-01-10 13:35:39 +00:00
|
|
|
/* First two cases. */
|
1986-10-20 10:17:57 +00:00
|
|
|
relo->or_nami = addrelo(relo, names, &valu);
|
2018-09-14 07:28:35 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
1985-01-10 13:35:39 +00:00
|
|
|
/*
|
|
|
|
* Third case: it is absolute. The relocation of absolute
|
|
|
|
* names is always 0. We only need to change the index.
|
|
|
|
*/
|
|
|
|
relo->or_nami = NLocals + NGlobals + outhead.oh_nsect;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If relocation is pc-relative, we had to update the value by
|
|
|
|
* the change in distance between the referencING and referencED
|
|
|
|
* section. We already added the origin of the referencED section;
|
|
|
|
* now we subtract the origin of the referencING section.
|
|
|
|
*/
|
|
|
|
if (relo->or_type & RELPC)
|
2018-09-14 07:28:35 +00:00
|
|
|
valu -= relorig[sectindex].org_size + outsect[sectindex].os_base;
|
1985-01-10 13:35:39 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Now put the value back.
|
|
|
|
*/
|
Reduce warnings, adjust format strings in util/led
Calls like `debug("something\n", 0, 0, 0, 0)` cause clang warnings,
because debug() is a macro that passes its arguments to printf(), and
clang warns about extra 0s to printf(). Silence the warnings by
hiding the printf() in a new function do_debug(). The code still
passes extra 0s to printf(), but clang can't warn.
Macros debug() and verbose() should use C99 __VA_ARGS__, so they don't
require the extra 0s; but ACK doesn't use __VA_ARGS__ yet.
Adjust some format strings for debug() or fatal(), or cast their
arguments, to match their types. I don't know whether uint32_t is
unsigned int or unsigned long, so I cast it to unsigned long, and
print it with "%lx".
In util/led/sym.c, #include "save.h" to declare savechar(), and use
parentheses to silence a clang warning in hash().
2019-11-01 22:27:34 +00:00
|
|
|
debug("written fixed up relocation to 0x%08lx type 0x%x value 0x%08lx\n",
|
|
|
|
(unsigned long)realaddress, relo->or_type, valu, 0);
|
1987-05-21 10:06:14 +00:00
|
|
|
putvalu(valu, emit + (relo->or_addr - off), relo->or_type);
|
1985-01-10 13:35:39 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We must change the offset within the section of the value to be
|
|
|
|
* relocated to its offset in the new section. `Or_addr' must again be
|
|
|
|
* in the normal part, of course.
|
|
|
|
*/
|
1986-10-20 10:17:57 +00:00
|
|
|
relo->or_addr += relorig[sectindex].org_size;
|
1985-01-10 13:35:39 +00:00
|
|
|
}
|