[Nasm-commits] [nasm:reldef] Make it possible to generate PC-relative relocations in data

nasm-bot for H. Peter Anvin hpa at zytor.com
Thu Jun 4 19:56:38 PDT 2020


Commit-ID:  80cc2029d31f4a7f1cd4334b9cfaf9556f0163ce
Gitweb:     http://repo.or.cz/w/nasm.git?a=commitdiff;h=80cc2029d31f4a7f1cd4334b9cfaf9556f0163ce
Author:     H. Peter Anvin <hpa at zytor.com>
AuthorDate: Tue, 4 Oct 2016 06:29:25 -0700
Committer:  H. Peter Anvin <hpa at zytor.com>
CommitDate: Tue, 4 Oct 2016 06:29:25 -0700

Make it possible to generate PC-relative relocations in data

Allow expressions such as:

      dd bar - $

... where "bar" is a symbol external to the segment.

Currently this is only supported for the Dx instructions; extending it
to inline immediates requires additional changes to the parser.  This
may, however, be sufficient for the needs of some users.

Signed-off-by: H. Peter Anvin <hpa at zytor.com>


---
 assemble.c      | 175 ++++++++++++++++++++++++++++++++++++--------------------
 nasm.h          |  12 ++--
 parser.c        |  72 ++++++++++++++++++-----
 test/reldef.asm |   9 +++
 4 files changed, 188 insertions(+), 80 deletions(-)

diff --git a/assemble.c b/assemble.c
index 0971fa3a..028d61cc 100644
--- a/assemble.c
+++ b/assemble.c
@@ -332,21 +332,46 @@ static int addrsize(enum out_type type, uint64_t size)
  */
 static void out(int64_t offset, int32_t segto, const void *data,
                 enum out_type type, uint64_t size,
-                int32_t segment, int32_t wrt)
+                int32_t segment, int32_t wrt, bool relative)
 {
     static int32_t lineno = 0;     /* static!!! */
     static const char *lnfname = NULL;
     uint8_t p[8];
-    int asize = addrsize(type, size); 	    /* Address size in bytes */
+    int asize = addrsize(type, size);	    /* Address size in bytes */
     const int amax  = ofmt->maxbits >> 3; /* Maximum address size in bytes */
 
+    if (unlikely(relative)) {
+        if (type == OUT_ADDRESS) {
+        switch (asize) {
+        case 1:
+            type = OUT_REL1ADR;
+            break;
+        case 2:
+            type = OUT_REL2ADR;
+            break;
+        case 4:
+            type = OUT_REL4ADR;
+            break;
+        case 8:
+            type = OUT_REL8ADR;
+            break;
+        default:
+            panic();
+        }
+
+        size = 0;
+        } else {
+            nasm_error(ERR_NONFATAL, "expression cannot be converted to relative");
+        }
+    }
+
     if (type == OUT_ADDRESS && segment == NO_SEG && wrt == NO_SEG) {
         /*
          * This is a non-relocated address, and we're going to
          * convert it into RAWDATA format.
          */
         uint8_t *q = p;
-        
+
         if (asize > 8) {
             nasm_panic(0, "OUT_ADDRESS with size > 8");
             return;
@@ -399,10 +424,11 @@ static void out_imm8(int64_t offset, int32_t segment,
 {
     if (opx->segment != NO_SEG) {
         uint64_t data = opx->offset;
-        out(offset, segment, &data, OUT_ADDRESS, asize, opx->segment, opx->wrt);
+        out(offset, segment, &data, OUT_ADDRESS, asize, opx->segment, opx->wrt,
+            opx->relative);
     } else {
         uint8_t byte = opx->offset;
-        out(offset, segment, &byte, OUT_RAWDATA, 1, NO_SEG, NO_SEG);
+        out(offset, segment, &byte, OUT_RAWDATA, 1, NO_SEG, NO_SEG, false);
     }
 }
 
@@ -475,7 +501,8 @@ int64_t assemble(int32_t segment, int64_t offset, int bits, iflag_t cp,
                                 " instruction");
                     } else {
                         out(offset, segment, &e->offset,
-                            OUT_ADDRESS, wsize, e->segment, e->wrt);
+                            OUT_ADDRESS, wsize, e->segment, e->wrt,
+                            e->relative);
                         offset += wsize;
                     }
                 } else if (e->type == EOT_DB_STRING ||
@@ -483,13 +510,13 @@ int64_t assemble(int32_t segment, int64_t offset, int bits, iflag_t cp,
                     int align;
 
                     out(offset, segment, e->stringval,
-                        OUT_RAWDATA, e->stringlen, NO_SEG, NO_SEG);
+                        OUT_RAWDATA, e->stringlen, NO_SEG, NO_SEG, false);
                     align = e->stringlen % wsize;
 
                     if (align) {
                         align = wsize - align;
                         out(offset, segment, zero_buffer,
-                            OUT_RAWDATA, align, NO_SEG, NO_SEG);
+                            OUT_RAWDATA, align, NO_SEG, NO_SEG, false);
                     }
                     offset += e->stringlen + align;
                 }
@@ -561,7 +588,7 @@ int64_t assemble(int32_t segment, int64_t offset, int bits, iflag_t cp,
                         break;
                     }
                     out(offset, segment, buf, OUT_RAWDATA, m,
-                        NO_SEG, NO_SEG);
+                        NO_SEG, NO_SEG, false);
                     l -= m;
                 }
             }
@@ -701,7 +728,7 @@ int64_t assemble(int32_t segment, int64_t offset, int bits, iflag_t cp,
                     }
                     if (c != 0) {
                         out(offset, segment, &c, OUT_RAWDATA, 1,
-                            NO_SEG, NO_SEG);
+                            NO_SEG, NO_SEG, false);
                         offset++;
                     }
                 }
@@ -918,6 +945,12 @@ static void bad_hle_warn(const insn * ins, uint8_t hleok)
     }
 }
 
+static void fail_if_relative(const struct operand *op)
+{
+    if (op->relative)
+        nasm_error(ERR_NONFATAL, "invalid use of self-relative operand");
+}
+
 /* Common construct */
 #define case3(x) case (x): case (x)+1: case (x)+2
 #define case4(x) case3(x): case (x)+3
@@ -1416,7 +1449,7 @@ static inline unsigned int emit_rex(insn *ins, int32_t segment, int64_t offset,
             !(ins->rex & (REX_V | REX_EV)) &&
             !ins->rex_done) {
             int rex = (ins->rex & REX_MASK) | REX_P;
-            out(offset, segment, &rex, OUT_RAWDATA, 1, NO_SEG, NO_SEG);
+            out(offset, segment, &rex, OUT_RAWDATA, 1, NO_SEG, NO_SEG, false);
             ins->rex_done = true;
             return 1;
         }
@@ -1455,7 +1488,7 @@ static void gencode(int32_t segment, int64_t offset, int bits,
         case 03:
         case 04:
             offset += emit_rex(ins, segment, offset, bits);
-            out(offset, segment, codes, OUT_RAWDATA, c, NO_SEG, NO_SEG);
+            out(offset, segment, codes, OUT_RAWDATA, c, NO_SEG, NO_SEG, false);
             codes += c;
             offset += c;
             break;
@@ -1469,7 +1502,7 @@ static void gencode(int32_t segment, int64_t offset, int bits,
         case4(010):
             offset += emit_rex(ins, segment, offset, bits);
             bytes[0] = *codes++ + (regval(opx) & 7);
-            out(offset, segment, bytes, OUT_RAWDATA, 1, NO_SEG, NO_SEG);
+            out(offset, segment, bytes, OUT_RAWDATA, 1, NO_SEG, NO_SEG, false);
             offset += 1;
             break;
 
@@ -1497,7 +1530,7 @@ static void gencode(int32_t segment, int64_t offset, int bits,
             warn_overflow_opd(opx, 2);
             data = opx->offset;
             out(offset, segment, &data, OUT_ADDRESS, 2,
-                opx->segment, opx->wrt);
+                opx->segment, opx->wrt, opx->relative);
             offset += 2;
             break;
 
@@ -1509,7 +1542,7 @@ static void gencode(int32_t segment, int64_t offset, int bits,
             warn_overflow_opd(opx, size);
             data = opx->offset;
             out(offset, segment, &data, OUT_ADDRESS, size,
-                opx->segment, opx->wrt);
+                opx->segment, opx->wrt, opx->relative);
             offset += size;
             break;
 
@@ -1517,7 +1550,7 @@ static void gencode(int32_t segment, int64_t offset, int bits,
             warn_overflow_opd(opx, 4);
             data = opx->offset;
             out(offset, segment, &data, OUT_ADDRESS, 4,
-                opx->segment, opx->wrt);
+                opx->segment, opx->wrt, opx->relative);
             offset += 4;
             break;
 
@@ -1526,22 +1559,23 @@ static void gencode(int32_t segment, int64_t offset, int bits,
             size = ins->addr_size >> 3;
             warn_overflow_opd(opx, size);
             out(offset, segment, &data, OUT_ADDRESS, size,
-                opx->segment, opx->wrt);
+                opx->segment, opx->wrt, opx->relative);
             offset += size;
             break;
 
         case4(050):
+            fail_if_relative(opx);
             if (opx->segment != segment) {
                 data = opx->offset;
                 out(offset, segment, &data,
                     OUT_REL1ADR, insn_end - offset,
-                    opx->segment, opx->wrt);
+                    opx->segment, opx->wrt, false);
             } else {
                 data = opx->offset - insn_end;
                 if (data > 127 || data < -128)
                     nasm_error(ERR_NONFATAL, "short jump is out of range");
                 out(offset, segment, &data,
-                    OUT_ADDRESS, 1, NO_SEG, NO_SEG);
+                    OUT_ADDRESS, 1, NO_SEG, NO_SEG, false);
             }
             offset += 1;
             break;
@@ -1549,25 +1583,27 @@ static void gencode(int32_t segment, int64_t offset, int bits,
         case4(054):
             data = (int64_t)opx->offset;
             out(offset, segment, &data, OUT_ADDRESS, 8,
-                opx->segment, opx->wrt);
+                opx->segment, opx->wrt, opx->relative);
             offset += 8;
             break;
 
         case4(060):
+            fail_if_relative(opx);
             if (opx->segment != segment) {
                 data = opx->offset;
                 out(offset, segment, &data,
                     OUT_REL2ADR, insn_end - offset,
-                    opx->segment, opx->wrt);
+                    opx->segment, opx->wrt, false);
             } else {
                 data = opx->offset - insn_end;
                 out(offset, segment, &data,
-                    OUT_ADDRESS, 2, NO_SEG, NO_SEG);
+                    OUT_ADDRESS, 2, NO_SEG, NO_SEG, false);
             }
             offset += 2;
             break;
 
         case4(064):
+            fail_if_relative(opx);
             if (opx->type & (BITS16 | BITS32 | BITS64))
                 size = (opx->type & BITS16) ? 2 : 4;
             else
@@ -1576,25 +1612,26 @@ static void gencode(int32_t segment, int64_t offset, int bits,
                 data = opx->offset;
                 out(offset, segment, &data,
                     size == 2 ? OUT_REL2ADR : OUT_REL4ADR,
-                    insn_end - offset, opx->segment, opx->wrt);
+                    insn_end - offset, opx->segment, opx->wrt, false);
             } else {
                 data = opx->offset - insn_end;
                 out(offset, segment, &data,
-                    OUT_ADDRESS, size, NO_SEG, NO_SEG);
+                    OUT_ADDRESS, size, NO_SEG, NO_SEG, false);
             }
             offset += size;
             break;
 
         case4(070):
+            fail_if_relative(opx);
             if (opx->segment != segment) {
                 data = opx->offset;
                 out(offset, segment, &data,
                     OUT_REL4ADR, insn_end - offset,
-                    opx->segment, opx->wrt);
+                    opx->segment, opx->wrt, false);
             } else {
                 data = opx->offset - insn_end;
                 out(offset, segment, &data,
-                    OUT_ADDRESS, 4, NO_SEG, NO_SEG);
+                    OUT_ADDRESS, 4, NO_SEG, NO_SEG, false);
             }
             offset += 4;
             break;
@@ -1606,7 +1643,7 @@ static void gencode(int32_t segment, int64_t offset, int bits,
             data = 0;
             out(offset, segment, &data, OUT_ADDRESS, 2,
                 ofmt->segbase(1 + opx->segment),
-                opx->wrt);
+                opx->wrt, opx->relative);
             offset += 2;
             break;
 
@@ -1641,7 +1678,7 @@ static void gencode(int32_t segment, int64_t offset, int bits,
         emit_is4:
             r = nasm_regvals[opx->basereg];
             bytes[0] = (r << 4) | ((r & 0x10) >> 1) | c;
-            out(offset, segment, bytes, OUT_RAWDATA, 1, NO_SEG, NO_SEG);
+            out(offset, segment, bytes, OUT_RAWDATA, 1, NO_SEG, NO_SEG, false);
             offset++;
             break;
 
@@ -1653,7 +1690,7 @@ static void gencode(int32_t segment, int64_t offset, int bits,
                         "signed dword immediate exceeds bounds");
             }
             out(offset, segment, &data, OUT_ADDRESS, -4,
-                opx->segment, opx->wrt);
+                opx->segment, opx->wrt, opx->relative);
             offset += 4;
             break;
 
@@ -1672,7 +1709,7 @@ static void gencode(int32_t segment, int64_t offset, int bits,
                        ((~ins->vexreg & 15) << 3) |
                        (1 << 2) | (ins->vex_wlp & 3);
             bytes[3] = ins->evex_p[2];
-            out(offset, segment, &bytes, OUT_RAWDATA, 4, NO_SEG, NO_SEG);
+            out(offset, segment, &bytes, OUT_RAWDATA, 4, NO_SEG, NO_SEG, false);
             offset += 4;
             break;
 
@@ -1685,13 +1722,13 @@ static void gencode(int32_t segment, int64_t offset, int bits,
                 bytes[1] = (ins->vex_cm & 31) | ((~ins->rex & 7) << 5);
                 bytes[2] = ((ins->rex & REX_W) << (7-3)) |
                     ((~ins->vexreg & 15)<< 3) | (ins->vex_wlp & 07);
-                out(offset, segment, &bytes, OUT_RAWDATA, 3, NO_SEG, NO_SEG);
+                out(offset, segment, &bytes, OUT_RAWDATA, 3, NO_SEG, NO_SEG, false);
                 offset += 3;
             } else {
                 bytes[0] = 0xc5;
                 bytes[1] = ((~ins->rex & REX_R) << (7-2)) |
                     ((~ins->vexreg & 15) << 3) | (ins->vex_wlp & 07);
-                out(offset, segment, &bytes, OUT_RAWDATA, 2, NO_SEG, NO_SEG);
+                out(offset, segment, &bytes, OUT_RAWDATA, 2, NO_SEG, NO_SEG, false);
                 offset += 2;
             }
             break;
@@ -1733,11 +1770,11 @@ static void gencode(int32_t segment, int64_t offset, int bits,
             if (opx->segment != NO_SEG) {
                 data = uv;
                 out(offset, segment, &data, OUT_ADDRESS, 1,
-                    opx->segment, opx->wrt);
+                    opx->segment, opx->wrt, opx->relative);
             } else {
                 bytes[0] = uv;
                 out(offset, segment, bytes, OUT_RAWDATA, 1, NO_SEG,
-                    NO_SEG);
+                    NO_SEG, false);
             }
             offset += 1;
             break;
@@ -1749,7 +1786,7 @@ static void gencode(int32_t segment, int64_t offset, int bits,
         case 0310:
             if (bits == 32 && !has_prefix(ins, PPS_ASIZE, P_A16)) {
                 *bytes = 0x67;
-                out(offset, segment, bytes, OUT_RAWDATA, 1, NO_SEG, NO_SEG);
+                out(offset, segment, bytes, OUT_RAWDATA, 1, NO_SEG, NO_SEG, false);
                 offset += 1;
             } else
                 offset += 0;
@@ -1758,7 +1795,7 @@ static void gencode(int32_t segment, int64_t offset, int bits,
         case 0311:
             if (bits != 32 && !has_prefix(ins, PPS_ASIZE, P_A32)) {
                 *bytes = 0x67;
-                out(offset, segment, bytes, OUT_RAWDATA, 1, NO_SEG, NO_SEG);
+                out(offset, segment, bytes, OUT_RAWDATA, 1, NO_SEG, NO_SEG, false);
                 offset += 1;
             } else
                 offset += 0;
@@ -1794,7 +1831,7 @@ static void gencode(int32_t segment, int64_t offset, int bits,
 
         case 0330:
             *bytes = *codes++ ^ get_cond_opcode(ins->condition);
-            out(offset, segment, bytes, OUT_RAWDATA, 1, NO_SEG, NO_SEG);
+            out(offset, segment, bytes, OUT_RAWDATA, 1, NO_SEG, NO_SEG, false);
             offset += 1;
             break;
 
@@ -1804,14 +1841,14 @@ static void gencode(int32_t segment, int64_t offset, int bits,
         case 0332:
         case 0333:
             *bytes = c - 0332 + 0xF2;
-            out(offset, segment, bytes, OUT_RAWDATA, 1, NO_SEG, NO_SEG);
+            out(offset, segment, bytes, OUT_RAWDATA, 1, NO_SEG, NO_SEG, false);
             offset += 1;
             break;
 
         case 0334:
             if (ins->rex & REX_R) {
                 *bytes = 0xF0;
-                out(offset, segment, bytes, OUT_RAWDATA, 1, NO_SEG, NO_SEG);
+                out(offset, segment, bytes, OUT_RAWDATA, 1, NO_SEG, NO_SEG, false);
                 offset += 1;
             }
             ins->rex &= ~(REX_L|REX_R);
@@ -1831,7 +1868,7 @@ static void gencode(int32_t segment, int64_t offset, int bits,
                 int64_t size = ins->oprs[0].offset;
                 if (size > 0)
                     out(offset, segment, NULL,
-                        OUT_RESERVE, size, NO_SEG, NO_SEG);
+                        OUT_RESERVE, size, NO_SEG, NO_SEG, false);
                 offset += size;
             }
             break;
@@ -1844,7 +1881,7 @@ static void gencode(int32_t segment, int64_t offset, int bits,
 
         case 0361:
             bytes[0] = 0x66;
-            out(offset, segment, bytes, OUT_RAWDATA, 1, NO_SEG, NO_SEG);
+            out(offset, segment, bytes, OUT_RAWDATA, 1, NO_SEG, NO_SEG, false);
             offset += 1;
             break;
 
@@ -1855,7 +1892,7 @@ static void gencode(int32_t segment, int64_t offset, int bits,
         case 0366:
         case 0367:
             *bytes = c - 0366 + 0x66;
-            out(offset, segment, bytes, OUT_RAWDATA, 1, NO_SEG, NO_SEG);
+            out(offset, segment, bytes, OUT_RAWDATA, 1, NO_SEG, NO_SEG, false);
             offset += 1;
             break;
 
@@ -1864,7 +1901,7 @@ static void gencode(int32_t segment, int64_t offset, int bits,
 
         case 0373:
             *bytes = bits == 16 ? 3 : 5;
-            out(offset, segment, bytes, OUT_RAWDATA, 1, NO_SEG, NO_SEG);
+            out(offset, segment, bytes, OUT_RAWDATA, 1, NO_SEG, NO_SEG, false);
             offset += 1;
             break;
 
@@ -1920,7 +1957,7 @@ static void gencode(int32_t segment, int64_t offset, int bits,
                     *p++ = ea_data.sib;
 
                 s = p - bytes;
-                out(offset, segment, bytes, OUT_RAWDATA, s, NO_SEG, NO_SEG);
+                out(offset, segment, bytes, OUT_RAWDATA, s, NO_SEG, NO_SEG, false);
 
                 /*
                  * Make sure the address gets the right offset in case
@@ -1939,11 +1976,16 @@ static void gencode(int32_t segment, int64_t offset, int bits,
                             if (overflow_signed(data, ea_data.bytes))
                                 warn_overflow(ERR_PASS2, ea_data.bytes);
                             out(offset, segment, &data, OUT_ADDRESS,
-                                ea_data.bytes, NO_SEG, NO_SEG);
+                                ea_data.bytes, NO_SEG, NO_SEG, false);
                         } else {
                             /* overflow check in output/linker? */
-                            out(offset, segment, &data, OUT_REL4ADR,
-                                insn_end - offset, opy->segment, opy->wrt);
+                            out(offset, segment, &data,
+                                ea_data.bytes == 1 ? OUT_REL1ADR :
+                                ea_data.bytes == 2 ? OUT_REL2ADR :
+                                ea_data.bytes == 4 ? OUT_REL4ADR :
+                                OUT_REL8ADR,
+                                insn_end - offset, opy->segment, opy->wrt,
+                                false);
                         }
                     } else {
                         int asize = ins->addr_size >> 3;
@@ -1963,7 +2005,7 @@ static void gencode(int32_t segment, int64_t offset, int bits,
                         }
 
                         out(offset, segment, &data, OUT_ADDRESS,
-                            atype, opy->segment, opy->wrt);
+                            atype, opy->segment, opy->wrt, false);
                     }
                 }
                 offset += s;
@@ -2490,17 +2532,26 @@ static enum ea_type process_ea(operand *input, ea *output, int bits,
             /*
              * It's a pure offset.
              */
-            if (bits == 64 && ((input->type & IP_REL) == IP_REL) &&
-                input->segment == NO_SEG) {
-                nasm_error(ERR_WARNING | ERR_PASS1, "absolute address can not be RIP-relative");
-                input->type &= ~IP_REL;
-                input->type |= MEMORY;
-            }
+            if (bits == 64 && !(IP_REL & ~input->type)) {
+                if (input->segment == NO_SEG) {
+                    nasm_error(ERR_WARNING | ERR_PASS1,
+                               "absolute address can not be RIP-relative");
+                    input->type &= ~IP_REL;
+                    input->type |= MEMORY;
+                }
+
+                if (input->relative) {
+                    nasm_error(ERR_WARNING | ERR_PASS1,
+                               "double relative address; ignoring REL");
+                    input->type &= ~IP_REL;
+                    input->type |= MEMORY;
+                }
+
+                if (eaflags & EAF_MIB) {
+                    nasm_error(ERR_NONFATAL, "RIP-relative addressing is prohibited for mib.");
+                    return -1;
+                }
 
-            if (bits == 64 &&
-                !(IP_REL & ~input->type) && (eaflags & EAF_MIB)) {
-                nasm_error(ERR_NONFATAL, "RIP-relative addressing is prohibited for mib.");
-                return -1;
             }
 
             if (eaflags & EAF_BYTEOFFS ||
@@ -2514,12 +2565,12 @@ static enum ea_type process_ea(operand *input, ea *output, int bits,
                 output->sib         = GEN_SIB(0, 4, 5);
                 output->bytes       = 4;
                 output->modrm       = GEN_MODRM(0, rfield, 4);
-                output->rip         = false;
+                output->rip         = input->relative;
             } else {
                 output->sib_present = false;
                 output->bytes       = (addrbits != 16 ? 4 : 2);
                 output->modrm       = GEN_MODRM(0, rfield, (addrbits != 16 ? 5 : 6));
-                output->rip         = bits == 64;
+                output->rip         = (bits == 64) || input->relative;
             }
         } else {
             /*
@@ -2531,6 +2582,8 @@ static enum ea_type process_ea(operand *input, ea *output, int bits,
             int t, it, bt;              /* register numbers */
             opflags_t x, ix, bx;        /* register flags */
 
+            output->rip = input->relative;
+
             if (s == 0)
                 i = -1;         /* make this easy, at least */
 
@@ -2617,7 +2670,7 @@ static enum ea_type process_ea(operand *input, ea *output, int bits,
                 default:   /* then what the smeg is it? */
                     goto err;    /* panic */
                 }
-                
+
                 if (bt == -1) {
                     base = 5;
                     mod = 0;
diff --git a/nasm.h b/nasm.h
index 663cc21e..0c6478f5 100644
--- a/nasm.h
+++ b/nasm.h
@@ -1,5 +1,5 @@
 /* ----------------------------------------------------------------------- *
- *   
+ *
  *   Copyright 1996-2016 The NASM Authors - All Rights Reserved
  *   See the file AUTHORS included with the NASM distribution for
  *   the specific copyright holders.
@@ -14,7 +14,7 @@
  *     copyright notice, this list of conditions and the following
  *     disclaimer in the documentation and/or other materials provided
  *     with the distribution.
- *     
+ *
  *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
@@ -31,7 +31,7 @@
  *
  * ----------------------------------------------------------------------- */
 
-/* 
+/*
  * nasm.h   main header file for the Netwide Assembler: inter-module interface
  */
 
@@ -546,6 +546,7 @@ typedef struct operand { /* operand to an instruction */
     int32_t         segment;    /* immediate segment, if needed */
     int64_t         offset;     /* any immediate number */
     int32_t         wrt;        /* segment base it's relative to */
+    bool            relative;   /* self-relative expression */
     int             eaflags;    /* special EA flags */
     int             opflags;    /* see OPFLAG_* defines below */
     decoflags_t     decoflags;  /* decorator flags such as {...} */
@@ -553,7 +554,7 @@ typedef struct operand { /* operand to an instruction */
 
 #define OPFLAG_FORWARD      1   /* operand is a forward reference */
 #define OPFLAG_EXTERN       2   /* operand is an external reference */
-#define OPFLAG_UNKNOWN      4   /* operand is an unknown reference 
+#define OPFLAG_UNKNOWN      4   /* operand is an unknown reference
                                  * (always a forward reference also)
                                  */
 
@@ -564,6 +565,7 @@ typedef struct extop { /* extended operand */
     int64_t         offset;     /* ... it's given here ... */
     int32_t         segment;    /* if it's a number/address, then... */
     int32_t         wrt;        /* ... and here */
+    bool            relative;   /* self-relative expression */
     enum extop_type type;       /* defined above */
 } extop;
 
@@ -678,7 +680,7 @@ struct ofmt {
     /*
      * Output format flags.
      */
-#define OFMT_TEXT   1   	/* Text file format */
+#define OFMT_TEXT   1		/* Text file format */
     unsigned int flags;
 
     int maxbits;                /* Maximum segment bits supported */
diff --git a/parser.c b/parser.c
index 5107860e..23eb09e7 100644
--- a/parser.c
+++ b/parser.c
@@ -1,6 +1,6 @@
 /* ----------------------------------------------------------------------- *
  *
- *   Copyright 1996-2013 The NASM Authors - All Rights Reserved
+ *   Copyright 1996-2016 The NASM Authors - All Rights Reserved
  *   See the file AUTHORS included with the NASM distribution for
  *   the specific copyright holders.
  *
@@ -261,7 +261,7 @@ static int parse_mref(operand *op, const expr *e)
 
         if (is_gpr && e->value == 1)
             b = e->type;	/* It can be basereg */
-        else            	/* No, it has to be indexreg */
+        else			/* No, it has to be indexreg */
             i = e->type, s = e->value;
         e++;
     }
@@ -382,6 +382,59 @@ static void mref_set_optype(operand *op)
     }
 }
 
+/*
+ * Convert an expression vector returned from evaluate() into an
+ * extop structure.  Return zero on success.
+ */
+static int value_to_extop(expr * vect, extop *eop, int32_t myseg)
+{
+    eop->type = EOT_DB_NUMBER;
+    eop->offset = 0;
+    eop->segment = eop->wrt = NO_SEG;
+    eop->relative = false;
+
+    for (; vect->type; vect++) {
+        if (!vect->value)       /* zero term, safe to ignore */
+            continue;
+
+        if (vect->type < EXPR_SIMPLE)  /* false if a register is present */
+            return -1;
+
+        if (vect->type == EXPR_UNKNOWN) /* something we can't resolve yet */
+            return 0;
+
+        if (vect->type == EXPR_SIMPLE) {
+            /* Simple number expression */
+            eop->offset += vect->value;
+            continue;
+        }
+        if (eop->wrt == NO_SEG && !eop->relative && vect->type == EXPR_WRT) {
+            /* WRT term */
+            eop->wrt = vect->value;
+            continue;
+        }
+
+        if (eop->wrt == NO_SEG && !eop->relative &&
+            vect->type == EXPR_SEGBASE + myseg && vect->value == -1) {
+            /* Expression of the form: foo - $ */
+            eop->relative = true;
+            continue;
+        }
+
+        if (eop->segment == NO_SEG && vect->type >= EXPR_SEGBASE &&
+            vect->value == 1) {
+            eop->segment = vect->type - EXPR_SEGBASE;
+            continue;
+        }
+
+        /* Otherwise, badness */
+        return -1;
+    }
+
+    /* We got to the end and it was all okay */
+    return 0;
+}
+
 insn *parse_line(int pass, char *buffer, insn *result, ldfunc ldef)
 {
     bool insn_is_label = false;
@@ -659,19 +712,10 @@ is_expression:
                 i = tokval.t_type;
                 if (!value)                  /* Error in evaluator */
                     goto fail;
-                if (is_unknown(value)) {
-                    eop->type = EOT_DB_NUMBER;
-                    eop->offset = 0;    /* doesn't matter what we put */
-                    eop->segment = eop->wrt = NO_SEG;   /* likewise */
-                } else if (is_reloc(value)) {
-                    eop->type = EOT_DB_NUMBER;
-                    eop->offset = reloc_value(value);
-                    eop->segment = reloc_seg(value);
-                    eop->wrt = reloc_wrt(value);
-                } else {
+                if (value_to_extop(value, eop, location->segment)) {
                     nasm_error(ERR_NONFATAL,
-                          "operand %d: expression is not simple"
-                          " or relocatable", oper_num);
+                               "operand %d: expression is not simple or relocatable",
+                               oper_num);
                 }
             }
 
diff --git a/test/reldef.asm b/test/reldef.asm
new file mode 100644
index 00000000..17186c57
--- /dev/null
+++ b/test/reldef.asm
@@ -0,0 +1,9 @@
+	bits 64
+
+	extern bar
+
+	section .data
+foo:	dd bar
+	dd foo - $
+;	dd foo*2
+	dd bar - $


More information about the Nasm-commits mailing list