In PowerPC as, check relocations with hi16/ha16/lo16.
authorGeorge Koehler <xkernigh@netscape.net>
Wed, 1 Feb 2017 15:36:06 +0000 (10:36 -0500)
committerGeorge Koehler <xkernigh@netscape.net>
Wed, 1 Feb 2017 15:36:06 +0000 (10:36 -0500)
Check that each relocation meets the requirements for RELOPPC, which
handles a hi16/lo16 or ha16/lo16 pair.

In the code, one might confuse hi_token with hl_token and hi_expr with
hl_expr, but I didn't think of better names for these variables.

mach/powerpc/as/mach0.c
mach/powerpc/as/mach1.c
mach/powerpc/as/mach5.c

index 1c20517..4e2e4eb 100644 (file)
@@ -25,6 +25,9 @@
 #undef ALIGNSECT
 #define ALIGNSECT      4
 
+#undef machfinish
+/* machfinish() in mach5.c */
+
 #undef VALWIDTH
 #define VALWIDTH       8
 
index 50f7996..10e7a2b 100644 (file)
@@ -10,3 +10,4 @@
 void no_hl(void);
 word_t eval_hl(struct expr_t* expr, int token);
 void emit_hl(word_t in);
+void machfinish(int n);
index 222508d..3029b7d 100644 (file)
@@ -1,6 +1,22 @@
+/*
+ * 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.
+ */
+
+/* Info about the current hi16, ha16, or lo16 */
 static int hl_token;
 static expr_t hl_expr;
 
+/* Info about the last hi16 or ha16 */
+static int hi_token = 0;
+static expr_t hi_expr;
+static valu_t hi_relonami;
+static long hi_lineno;
+static ADDR_T hi_dotval;
+static short hi_dottyp;
+
 void no_hl(void) {
     hl_token = 0;
 }
@@ -33,26 +49,138 @@ word_t eval_hl(expr_t* expr, int token)
     }
 }
 
+static const char *hi_name(void)
+{
+    return hi_token == OP_HI ? "hi16" : "ha16";
+}
+
+static void cant_use_hl(const char *what)
+{
+    serror("can't use %s in this instruction", what);
+}
+
+static void check_hl_expr(void)
+{
+    int und = ((hi_expr.typ & S_TYP) == S_UND);
+    int com = (hi_expr.typ & S_COM);
+
+    if (hi_expr.typ != hl_expr.typ ||
+        hi_expr.val != hl_expr.val ||
+        ((und || com) && hi_relonami != relonami)) {
+
+        long old = lineno;
+        lineno = hi_lineno;
+        serror("%s and lo16 must use same expression", hi_name());
+        lineno = old;
+    }
+}
+
+static void hi_missing_lo(void)
+{
+    long old = lineno;
+    lineno = hi_lineno;
+    serror("%s needs lo16 in next instruction", hi_name());
+    lineno = old;
+}
+
+static void need_hi(const char *needed, const char *got)
+{
+    long old = lineno;
+    lineno = hi_lineno;
+    serror("need %s, not %s, here", needed, got);
+    lineno = old;
+}
+
 void emit_hl(word_t in)
 {
+    word_t opcode;
     int type;
 
     switch (hl_token) {
     case OP_HI:
     case OP_HA:
+        /* hi16[...] or ha16[...] */
         type = hl_expr.typ & S_TYP;
-        if (type != S_ABS)
+        if (type != S_ABS && PASS_RELO) {
+            if (hi_token)
+                hi_missing_lo();
+
+            opcode = in >> 26;
+            if (opcode != 15 /* addis */)
+                cant_use_hl(hi_name());
+
+            /* This hi16 or ha16 needs a lo16 in the next instruction.
+             * Save this info until we find the lo16.
+             */
+            hi_token = hl_token;
+            hi_expr = hl_expr;
+            hi_relonami = relonami;
+            hi_lineno = lineno;
+            hi_dotval = DOTVAL;
+            hi_dottyp = DOTTYP;
+
+            /* Relocate this hi16 or ha16 and the next lo16. */
             newrelo(hl_expr.typ, RELOPPC | FIXUPFLAGS);
-       break;
+        }
+        break;
     case OP_LO:
-        /* If the assembler stored a symbol for relocation later, we
-         * need to abandon it (because the relocation was generated by
-         * hi16 or ha16). */
+        /* lo16[...] */
         type = hl_expr.typ & S_TYP;
-        if (type != S_ABS)
-            relonami = 0;
-       break;
+        if (type != S_ABS && PASS_RELO) {
+            if (hi_token) {
+                /* The last hi16 or ha16 needs this lo16 in the next
+                 * instruction, to complete the pair for RELOPPC.
+                 */
+                if (DOTVAL != hi_dotval + 4 || DOTTYP != hi_dottyp)
+                    hi_missing_lo();
+
+                check_hl_expr();
+
+                opcode = in >> 26;
+                switch (opcode) {
+                case 14: /* addi */
+                case 34: /* lbz */
+                case 48: /* lfs */
+                case 50: /* lfd */
+                case 42: /* lha */
+                case 40: /* lhz */
+                case 32: /* lwz */
+                case 38: /* stb */
+                case 52: /* stfs */
+                case 54: /* stfd */
+                case 44: /* sth */
+                case 36: /* stw */
+                    if (hi_token == OP_HI)
+                        need_hi("ha16", "hi16");
+                    break;
+                case 24: /* ori */
+                    if (hi_token == OP_HA)
+                        need_hi("hi16", "ha16");
+                    break;
+                default:
+                    cant_use_hl("lo16");
+                }
+
+                hi_token = 0;
+
+                /* If we have a symbol for relocation, we need to
+                 * abandon it (because the relocation was generated by
+                 * hi16 or ha16).
+                 */
+                relonami = 0;
+            } else {
+                serror("lo16 without hi16 or ha16");
+            }
+        }
+        break;
     }
 
     emit4(in);
 }
+
+void machfinish(int n) {
+    if (hi_token) {
+        hi_missing_lo();
+        hi_token = 0;
+    }
+}