Add missing softfloat helpers.
[qemu] / dyngen.c
index 5823e07..953b36c 100644 (file)
--- a/dyngen.c
+++ b/dyngen.c
 typedef int32_t host_long;
 typedef uint32_t host_ulong;
 #define swabls(x) swab32s(x)
+#define swablss(x) swab32ss(x)
 #else
 typedef int64_t host_long;
 typedef uint64_t host_ulong;
 #define swabls(x) swab64s(x)
+#define swablss(x) swab64ss(x)
 #endif
 
 #ifdef ELF_USES_RELOCA
@@ -146,11 +148,11 @@ typedef uint64_t host_ulong;
 
 #ifdef CONFIG_FORMAT_COFF
 
-#include "a.out.h"
-
 typedef int32_t host_long;
 typedef uint32_t host_ulong;
 
+#include "a.out.h"
+
 #define FILENAMELEN 256
 
 typedef struct coff_sym {
@@ -284,11 +286,21 @@ void swab32s(uint32_t *p)
     *p = bswap32(*p);
 }
 
+void swab32ss(int32_t *p)
+{
+    *p = bswap32(*p);
+}
+
 void swab64s(uint64_t *p)
 {
     *p = bswap64(*p);
 }
 
+void swab64ss(int64_t *p)
+{
+    *p = bswap64(*p);
+}
+
 uint16_t get16(uint16_t *p)
 {
     uint16_t val;
@@ -397,7 +409,7 @@ void elf_swap_rel(ELF_RELOC *rel)
     swabls(&rel->r_offset);
     swabls(&rel->r_info);
 #ifdef ELF_USES_RELOCA
-    swabls(&rel->r_addend);
+    swablss(&rel->r_addend);
 #endif
 }
 
@@ -505,7 +517,7 @@ int load_object(const char *filename)
     }
 
     sec = &shdr[ehdr.e_shstrndx];
-    shstr = sdata[ehdr.e_shstrndx];
+    shstr = (char *)sdata[ehdr.e_shstrndx];
 
     /* swap relocations */
     for(i = 0; i < ehdr.e_shnum; i++) {
@@ -541,7 +553,7 @@ int load_object(const char *filename)
     strtab_sec = &shdr[symtab_sec->sh_link];
 
     symtab = (ElfW(Sym) *)sdata[symtab_sec - shdr];
-    strtab = sdata[symtab_sec->sh_link];
+    strtab = (char *)sdata[symtab_sec->sh_link];
     
     nb_syms = symtab_sec->sh_size / sizeof(ElfW(Sym));
     if (do_swap) {
@@ -635,6 +647,8 @@ static char *get_rel_sym_name(EXE_RELOC *rel)
     name = get_sym_name(symtab + *(uint32_t *)(rel->r_reloc->r_symndx));
     if (!strcmp(name, ".data"))
         name = name_for_dotdata(rel);
+    if (name[0] == '.')
+        return NULL;
     return name;
 }
 
@@ -1194,7 +1208,7 @@ void get_reloc_expr(char *name, int name_size, const char *sym_name)
     } else {
 #ifdef HOST_SPARC
         if (sym_name[0] == '.')
-            snprintf(name, sizeof(name),
+            snprintf(name, name_size,
                      "(long)(&__dot_%s)",
                      sym_name + 1);
         else
@@ -1203,6 +1217,48 @@ void get_reloc_expr(char *name, int name_size, const char *sym_name)
     }
 }
 
+#ifdef HOST_IA64
+
+#define PLT_ENTRY_SIZE 16      /* 1 bundle containing "brl" */
+
+struct plt_entry {
+    struct plt_entry *next;
+    const char *name;
+    unsigned long addend;
+} *plt_list;
+
+static int
+get_plt_index (const char *name, unsigned long addend)
+{
+    struct plt_entry *plt, *prev= NULL;
+    int index = 0;
+
+    /* see if we already have an entry for this target: */
+    for (plt = plt_list; plt; ++index, prev = plt, plt = plt->next)
+       if (strcmp(plt->name, name) == 0 && plt->addend == addend)
+           return index;
+
+    /* nope; create a new PLT entry: */
+
+    plt = malloc(sizeof(*plt));
+    if (!plt) {
+       perror("malloc");
+       exit(1);
+    }
+    memset(plt, 0, sizeof(*plt));
+    plt->name = strdup(name);
+    plt->addend = addend;
+
+    /* append to plt-list: */
+    if (prev)
+       prev->next = plt;
+    else
+       plt_list = plt;
+    return index;
+}
+
+#endif
+
 #ifdef HOST_ARM
 
 int arm_emit_ldr_info(const char *name, unsigned long start_offset,
@@ -1211,90 +1267,149 @@ int arm_emit_ldr_info(const char *name, unsigned long start_offset,
 {
     uint8_t *p;
     uint32_t insn;
-    int offset, min_offset, pc_offset, data_size;
+    int offset, min_offset, pc_offset, data_size, spare, max_pool;
     uint8_t data_allocated[1024];
     unsigned int data_index;
+    int type;
     
     memset(data_allocated, 0, sizeof(data_allocated));
     
     p = p_start;
     min_offset = p_end - p_start;
+    spare = 0x7fffffff;
     while (p < p_start + min_offset) {
         insn = get32((uint32_t *)p);
+        /* TODO: Armv5e ldrd.  */
+        /* TODO: VFP load.  */
         if ((insn & 0x0d5f0000) == 0x051f0000) {
             /* ldr reg, [pc, #im] */
             offset = insn & 0xfff;
             if (!(insn & 0x00800000))
-                        offset = -offset;
+                offset = -offset;
+            max_pool = 4096;
+            type = 0;
+        } else if ((insn & 0x0e5f0f00) == 0x0c1f0100) {
+            /* FPA ldf.  */
+            offset = (insn & 0xff) << 2;
+            if (!(insn & 0x00800000))
+                offset = -offset;
+            max_pool = 1024;
+            type = 1;
+        } else if ((insn & 0x0fff0000) == 0x028f0000) {
+            /* Some gcc load a doubleword immediate with
+               add regN, pc, #imm
+               ldmia regN, {regN, regM}
+               Hope and pray the compiler never generates somethin like
+               add reg, pc, #imm1; ldr reg, [reg, #-imm2]; */
+            int r;
+
+            r = (insn & 0xf00) >> 7;
+            offset = ((insn & 0xff) >> r) | ((insn & 0xff) << (32 - r));
+            max_pool = 1024;
+            type = 2;
+        } else {
+            max_pool = 0;
+            type = -1;
+        }
+        if (type >= 0) {
+            /* PC-relative load needs fixing up.  */
+            if (spare > max_pool - offset)
+                spare = max_pool - offset;
             if ((offset & 3) !=0)
-                error("%s:%04x: ldr pc offset must be 32 bit aligned", 
+                error("%s:%04x: pc offset must be 32 bit aligned", 
+                      name, start_offset + p - p_start);
+            if (offset < 0)
+                error("%s:%04x: Embedded literal value",
                       name, start_offset + p - p_start);
             pc_offset = p - p_start + offset + 8;
             if (pc_offset <= (p - p_start) || 
                 pc_offset >= (p_end - p_start))
-                error("%s:%04x: ldr pc offset must point inside the function code", 
+                error("%s:%04x: pc offset must point inside the function code", 
                       name, start_offset + p - p_start);
             if (pc_offset < min_offset)
                 min_offset = pc_offset;
             if (outfile) {
-                /* ldr position */
+                /* The intruction position */
                 fprintf(outfile, "    arm_ldr_ptr->ptr = gen_code_ptr + %d;\n", 
                         p - p_start);
-                /* ldr data index */
-                data_index = ((p_end - p_start) - pc_offset - 4) >> 2;
-                fprintf(outfile, "    arm_ldr_ptr->data_ptr = arm_data_ptr + %d;\n", 
+                /* The position of the constant pool data.  */
+                data_index = ((p_end - p_start) - pc_offset) >> 2;
+                fprintf(outfile, "    arm_ldr_ptr->data_ptr = arm_data_ptr - %d;\n", 
                         data_index);
+                fprintf(outfile, "    arm_ldr_ptr->type = %d;\n", type);
                 fprintf(outfile, "    arm_ldr_ptr++;\n");
-                if (data_index >= sizeof(data_allocated))
-                    error("%s: too many data", name);
-                if (!data_allocated[data_index]) {
-                    ELF_RELOC *rel;
-                    int i, addend, type;
-                    const char *sym_name, *p;
-                    char relname[1024];
-
-                    data_allocated[data_index] = 1;
-
-                    /* data value */
-                    addend = get32((uint32_t *)(p_start + pc_offset));
-                    relname[0] = '\0';
-                    for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
-                        if (rel->r_offset == (pc_offset + start_offset)) {
-                            sym_name = get_rel_sym_name(rel);
-                            /* the compiler leave some unnecessary references to the code */
-                            get_reloc_expr(relname, sizeof(relname), sym_name);
-                            type = ELF32_R_TYPE(rel->r_info);
-                            if (type != R_ARM_ABS32)
-                                error("%s: unsupported data relocation", name);
-                            break;
-                        }
-                    }
-                    fprintf(outfile, "    arm_data_ptr[%d] = 0x%x",
-                            data_index, addend);
-                    if (relname[0] != '\0')
-                        fprintf(outfile, " + %s", relname);
-                    fprintf(outfile, ";\n");
-                }
             }
         }
         p += 4;
     }
+
+    /* Copy and relocate the constant pool data.  */
     data_size = (p_end - p_start) - min_offset;
     if (data_size > 0 && outfile) {
-        fprintf(outfile, "    arm_data_ptr += %d;\n", data_size >> 2);
+        spare += min_offset;
+        fprintf(outfile, "    arm_data_ptr -= %d;\n", data_size >> 2);
+        fprintf(outfile, "    arm_pool_ptr -= %d;\n", data_size);
+        fprintf(outfile, "    if (arm_pool_ptr > gen_code_ptr + %d)\n"
+                         "        arm_pool_ptr = gen_code_ptr + %d;\n",
+                         spare, spare);
+
+        data_index = 0;
+        for (pc_offset = min_offset;
+             pc_offset < p_end - p_start;
+             pc_offset += 4) {
+
+            ELF_RELOC *rel;
+            int i, addend, type;
+            const char *sym_name;
+            char relname[1024];
+
+            /* data value */
+            addend = get32((uint32_t *)(p_start + pc_offset));
+            relname[0] = '\0';
+            for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
+                if (rel->r_offset == (pc_offset + start_offset)) {
+                    sym_name = get_rel_sym_name(rel);
+                    /* the compiler leave some unnecessary references to the code */
+                    get_reloc_expr(relname, sizeof(relname), sym_name);
+                    type = ELF32_R_TYPE(rel->r_info);
+                    if (type != R_ARM_ABS32)
+                        error("%s: unsupported data relocation", name);
+                    break;
+                }
+            }
+            fprintf(outfile, "    arm_data_ptr[%d] = 0x%x",
+                    data_index, addend);
+            if (relname[0] != '\0')
+                fprintf(outfile, " + %s", relname);
+            fprintf(outfile, ";\n");
+
+            data_index++;
+        }
     }
 
-    /* the last instruction must be a mov pc, lr */
     if (p == p_start)
         goto arm_ret_error;
     p -= 4;
     insn = get32((uint32_t *)p);
-    if ((insn & 0xffff0000) != 0xe91b0000) {
+    /* The last instruction must be an ldm instruction.  There are several
+       forms generated by gcc:
+        ldmib sp, {..., pc}  (implies a sp adjustment of +4)
+        ldmia sp, {..., pc}
+        ldmea fp, {..., pc} */
+    if ((insn & 0xffff8000) == 0xe99d8000) {
+        if (outfile) {
+            fprintf(outfile,
+                    "    *(uint32_t *)(gen_code_ptr + %d) = 0xe28dd004;\n",
+                    p - p_start);
+        }
+        p += 4;
+    } else if ((insn & 0xffff8000) != 0xe89d8000
+        && (insn & 0xffff8000) != 0xe91b8000) {
     arm_ret_error:
         if (!outfile)
             printf("%s: invalid epilog\n", name);
     }
-    return p - p_start;            
+    return p - p_start;
 }
 #endif
 
@@ -1392,10 +1507,19 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
         /* 08 00 84 00 */
         if (get32((uint32_t *)p) != 0x00840008)
             error("br.ret.sptk.many b0;; expected at the end of %s", name);
-        copy_size = p - p_start;
+       copy_size = p_end - p_start;
     }
 #elif defined(HOST_SPARC)
     {
+#define INSN_SAVE       0x9de3a000
+#define INSN_RET        0x81c7e008
+#define INSN_RETL       0x81c3e008
+#define INSN_RESTORE    0x81e80000
+#define INSN_RETURN     0x81cfe008
+#define INSN_NOP        0x01000000
+#define INSN_ADD_SP     0x9c03a000 // add %sp, nn, %sp
+#define INSN_SUB_SP     0x9c23a000 // sub %sp, nn, %sp
+
         uint32_t start_insn, end_insn1, end_insn2;
         uint8_t *p;
         p = (void *)(p_end - 8);
@@ -1404,13 +1528,21 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
         start_insn = get32((uint32_t *)(p_start + 0x0));
         end_insn1 = get32((uint32_t *)(p + 0x0));
         end_insn2 = get32((uint32_t *)(p + 0x4));
-        if ((start_insn & ~0x1fff) == 0x9de3a000) {
+        if (((start_insn & ~0x1fff) == INSN_SAVE) ||
+            (start_insn & ~0x1fff) == INSN_ADD_SP) {
             p_start += 0x4;
             start_offset += 0x4;
-            if ((int)(start_insn | ~0x1fff) < -128)
-                error("Found bogus save at the start of %s", name);
-            if (end_insn1 != 0x81c7e008 || end_insn2 != 0x81e80000)
+            if (end_insn1 == INSN_RET && end_insn2 == INSN_RESTORE)
+                /* SPARC v7: ret; restore; */ ;
+            else if (end_insn1 == INSN_RETURN && end_insn2 == INSN_NOP)
+                /* SPARC v9: return; nop; */ ;
+            else if (end_insn1 == INSN_RETL && (end_insn2 & ~0x1fff) == INSN_SUB_SP)
+                /* SPARC v7: retl; sub %sp, nn, %sp; */ ;
+            else
+
                 error("ret; restore; not found at end of %s", name);
+        } else if (end_insn1 == INSN_RETL && end_insn2 == INSN_NOP) {
+            ;
         } else {
             error("No save at the beginning of %s", name);
         }
@@ -1418,7 +1550,7 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
         /* Skip a preceeding nop, if present.  */
         if (p > p_start) {
             skip_insn = get32((uint32_t *)(p - 0x4));
-            if (skip_insn == 0x01000000)
+            if (skip_insn == INSN_NOP)
                 p -= 4;
         }
 #endif
@@ -1426,21 +1558,41 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
     }
 #elif defined(HOST_SPARC64)
     {
+#define INSN_SAVE       0x9de3a000
+#define INSN_RET        0x81c7e008
+#define INSN_RETL       0x81c3e008
+#define INSN_RESTORE    0x81e80000
+#define INSN_RETURN     0x81cfe008
+#define INSN_NOP        0x01000000
+#define INSN_ADD_SP     0x9c03a000 // add %sp, nn, %sp
+#define INSN_SUB_SP     0x9c23a000 // sub %sp, nn, %sp
+
         uint32_t start_insn, end_insn1, end_insn2, skip_insn;
         uint8_t *p;
         p = (void *)(p_end - 8);
+#if 0
+        /* XXX: check why it occurs */
         if (p <= p_start)
             error("empty code for %s", name);
+#endif
         start_insn = get32((uint32_t *)(p_start + 0x0));
         end_insn1 = get32((uint32_t *)(p + 0x0));
         end_insn2 = get32((uint32_t *)(p + 0x4));
-        if ((start_insn & ~0x1fff) == 0x9de3a000) {
+        if (((start_insn & ~0x1fff) == INSN_SAVE) ||
+            (start_insn & ~0x1fff) == INSN_ADD_SP) {
             p_start += 0x4;
             start_offset += 0x4;
-            if ((int)(start_insn | ~0x1fff) < -256)
-                error("Found bogus save at the start of %s", name);
-            if (end_insn1 != 0x81c7e008 || end_insn2 != 0x81e80000)
+            if (end_insn1 == INSN_RET && end_insn2 == INSN_RESTORE)
+                /* SPARC v7: ret; restore; */ ;
+            else if (end_insn1 == INSN_RETURN && end_insn2 == INSN_NOP)
+                /* SPARC v9: return; nop; */ ;
+            else if (end_insn1 == INSN_RETL && (end_insn2 & ~0x1fff) == INSN_SUB_SP)
+                /* SPARC v7: retl; sub %sp, nn, %sp; */ ;
+            else
+
                 error("ret; restore; not found at end of %s", name);
+        } else if (end_insn1 == INSN_RETL && end_insn2 == INSN_NOP) {
+            ;
         } else {
             error("No save at the beginning of %s", name);
         }
@@ -1456,6 +1608,8 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
     }
 #elif defined(HOST_ARM)
     {
+        uint32_t insn;
+
         if ((p_end - p_start) <= 16)
             error("%s: function too small", name);
         if (get32((uint32_t *)p_start) != 0xe1a0c00d ||
@@ -1464,6 +1618,12 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
             error("%s: invalid prolog", name);
         p_start += 12;
         start_offset += 12;
+        insn = get32((uint32_t *)p_start);
+        if ((insn & 0xffffff00) == 0xe24dd000) {
+            /* Stack adjustment.  Assume op uses the frame pointer.  */
+            p_start -= 4;
+            start_offset -= 4;
+        }
         copy_size = arm_emit_ldr_info(name, start_offset, NULL, p_start, p_end, 
                                       relocs, nb_relocs);
     }
@@ -1529,7 +1689,11 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
             }
             fprintf(outfile, ";\n");
         }
+#if defined(HOST_IA64)
+        fprintf(outfile, "    extern char %s;\n", name);
+#else
         fprintf(outfile, "    extern void %s();\n", name);
+#endif
 
         for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
             host_ulong offset = get_rel_offset(rel);
@@ -1550,9 +1714,18 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
                        continue;
                    }
 #endif
-#ifdef __APPLE__
+#if defined(__APPLE__)
 /* set __attribute((unused)) on darwin because we wan't to avoid warning when we don't use the symbol */
                     fprintf(outfile, "extern char %s __attribute__((unused));\n", sym_name);
+#elif defined(HOST_IA64)
+                       if (ELF64_R_TYPE(rel->r_info) != R_IA64_PCREL21B)
+                               /*
+                                * PCREL21 br.call targets generally
+                                * are out of range and need to go
+                                * through an "import stub".
+                                */
+                               fprintf(outfile, "    extern char %s;\n",
+                                       sym_name);
 #else
                     fprintf(outfile, "extern char %s;\n", sym_name);
 #endif
@@ -1567,7 +1740,7 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
         {
             EXE_SYM *sym;
             const char *sym_name, *p;
-            unsigned long val;
+            host_ulong val;
             int n;
 
             for(i = 0, sym = symtab; i < nb_syms; i++, sym++) {
@@ -1599,7 +1772,7 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
 #ifdef CONFIG_FORMAT_MACH
                     offset -= section_hdr[sym->n_sect-1].addr;
 #endif
-                    val = *(unsigned long *)(ptr + offset);
+                    val = *(host_ulong *)(ptr + offset);
 #ifdef ELF_USES_RELOCA
                     {
                         int reloc_shndx, nb_relocs1, j;
@@ -1622,7 +1795,7 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
 #endif                    
                     if (val >= start_offset && val <= start_offset + copy_size) {
                         n = strtol(p, NULL, 10);
-                        fprintf(outfile, "    label_offsets[%d] = %ld + (gen_code_ptr - gen_code_buf);\n", n, val - start_offset);
+                        fprintf(outfile, "    label_offsets[%d] = %ld + (gen_code_ptr - gen_code_buf);\n", n, (long)(val - start_offset));
                     }
                 }
             }
@@ -1639,10 +1812,14 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
                 char name[256];
                 int type;
                 int addend;
+                int reloc_offset;
                 for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
                 if (rel->r_offset >= start_offset &&
                    rel->r_offset < start_offset + copy_size) {
                     sym_name = get_rel_sym_name(rel);
+                    if (!sym_name)
+                        continue;
+                    reloc_offset = rel->r_offset - start_offset;
                     if (strstart(sym_name, "__op_jmp", &p)) {
                         int n;
                         n = strtol(p, NULL, 10);
@@ -1651,10 +1828,10 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
                            chaining: the offset of the instruction
                            needs to be stored */
                         fprintf(outfile, "    jmp_offsets[%d] = %d + (gen_code_ptr - gen_code_buf);\n",
-                                n, rel->r_offset - start_offset);
+                                n, reloc_offset);
                         continue;
                     }
-                        
+
                     get_reloc_expr(name, sizeof(name), sym_name);
                     addend = get32((uint32_t *)(text + rel->r_offset));
 #ifdef CONFIG_FORMAT_ELF
@@ -1662,11 +1839,11 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
                     switch(type) {
                     case R_386_32:
                         fprintf(outfile, "    *(uint32_t *)(gen_code_ptr + %d) = %s + %d;\n", 
-                                rel->r_offset - start_offset, name, addend);
+                                reloc_offset, name, addend);
                         break;
                     case R_386_PC32:
                         fprintf(outfile, "    *(uint32_t *)(gen_code_ptr + %d) = %s - (long)(gen_code_ptr + %d) + %d;\n", 
-                                rel->r_offset - start_offset, name, rel->r_offset - start_offset, addend);
+                                reloc_offset, name, reloc_offset, addend);
                         break;
                     default:
                         error("unsupported i386 relocation (%d)", type);
@@ -1689,11 +1866,11 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
                     switch(type) {
                     case DIR32:
                         fprintf(outfile, "    *(uint32_t *)(gen_code_ptr + %d) = %s + %d;\n", 
-                                rel->r_offset - start_offset, name, addend);
+                                reloc_offset, name, addend);
                         break;
                     case DISP32:
                         fprintf(outfile, "    *(uint32_t *)(gen_code_ptr + %d) = %s - (long)(gen_code_ptr + %d) + %d -4;\n", 
-                                rel->r_offset - start_offset, name, rel->r_offset - start_offset, addend);
+                                reloc_offset, name, reloc_offset, addend);
                         break;
                     default:
                         error("unsupported i386 relocation (%d)", type);
@@ -1709,6 +1886,7 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
                 char name[256];
                 int type;
                 int addend;
+                int reloc_offset;
                 for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
                 if (rel->r_offset >= start_offset &&
                    rel->r_offset < start_offset + copy_size) {
@@ -1716,18 +1894,19 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
                     get_reloc_expr(name, sizeof(name), sym_name);
                     type = ELF32_R_TYPE(rel->r_info);
                     addend = rel->r_addend;
+                    reloc_offset = rel->r_offset - start_offset;
                     switch(type) {
                     case R_X86_64_32:
                         fprintf(outfile, "    *(uint32_t *)(gen_code_ptr + %d) = (uint32_t)%s + %d;\n", 
-                                rel->r_offset - start_offset, name, addend);
+                                reloc_offset, name, addend);
                         break;
                     case R_X86_64_32S:
                         fprintf(outfile, "    *(uint32_t *)(gen_code_ptr + %d) = (int32_t)%s + %d;\n", 
-                                rel->r_offset - start_offset, name, addend);
+                                reloc_offset, name, addend);
                         break;
                     case R_X86_64_PC32:
                         fprintf(outfile, "    *(uint32_t *)(gen_code_ptr + %d) = %s - (long)(gen_code_ptr + %d) + %d;\n", 
-                                rel->r_offset - start_offset, name, rel->r_offset - start_offset, addend);
+                                reloc_offset, name, reloc_offset, addend);
                         break;
                     default:
                         error("unsupported X86_64 relocation (%d)", type);
@@ -1741,10 +1920,12 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
                 char name[256];
                 int type;
                 int addend;
+                int reloc_offset;
                 for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
                     if (rel->r_offset >= start_offset &&
                        rel->r_offset < start_offset + copy_size) {
                         sym_name = strtab + symtab[ELFW(R_SYM)(rel->r_info)].st_name;
+                        reloc_offset = rel->r_offset - start_offset;
                         if (strstart(sym_name, "__op_jmp", &p)) {
                             int n;
                             n = strtol(p, NULL, 10);
@@ -1753,7 +1934,7 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
                                chaining: the offset of the instruction
                                needs to be stored */
                             fprintf(outfile, "    jmp_offsets[%d] = %d + (gen_code_ptr - gen_code_buf);\n",
-                                    n, rel->r_offset - start_offset);
+                                    n, reloc_offset);
                             continue;
                         }
                         
@@ -1763,24 +1944,24 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
                         switch(type) {
                         case R_PPC_ADDR32:
                             fprintf(outfile, "    *(uint32_t *)(gen_code_ptr + %d) = %s + %d;\n", 
-                                    rel->r_offset - start_offset, name, addend);
+                                    reloc_offset, name, addend);
                             break;
                         case R_PPC_ADDR16_LO:
                             fprintf(outfile, "    *(uint16_t *)(gen_code_ptr + %d) = (%s + %d);\n", 
-                                    rel->r_offset - start_offset, name, addend);
+                                    reloc_offset, name, addend);
                             break;
                         case R_PPC_ADDR16_HI:
                             fprintf(outfile, "    *(uint16_t *)(gen_code_ptr + %d) = (%s + %d) >> 16;\n", 
-                                    rel->r_offset - start_offset, name, addend);
+                                    reloc_offset, name, addend);
                             break;
                         case R_PPC_ADDR16_HA:
                             fprintf(outfile, "    *(uint16_t *)(gen_code_ptr + %d) = (%s + %d + 0x8000) >> 16;\n", 
-                                    rel->r_offset - start_offset, name, addend);
+                                    reloc_offset, name, addend);
                             break;
                         case R_PPC_REL24:
                             /* warning: must be at 32 MB distancy */
                             fprintf(outfile, "    *(uint32_t *)(gen_code_ptr + %d) = (*(uint32_t *)(gen_code_ptr + %d) & ~0x03fffffc) | ((%s - (long)(gen_code_ptr + %d) + %d) & 0x03fffffc);\n", 
-                                    rel->r_offset - start_offset, rel->r_offset - start_offset, name, rel->r_offset - start_offset, addend);
+                                    reloc_offset, reloc_offset, name, reloc_offset, addend);
                             break;
                         default:
                             error("unsupported powerpc relocation (%d)", type);
@@ -1882,6 +2063,7 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
                 char name[256];
                 int type;
                 int addend;
+                int reloc_offset;
                 for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
                     if (rel->r_offset >= start_offset &&
                        rel->r_offset < start_offset + copy_size) {
@@ -1889,18 +2071,19 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
                         get_reloc_expr(name, sizeof(name), sym_name);
                         type = ELF32_R_TYPE(rel->r_info);
                         addend = rel->r_addend;
+                        reloc_offset = rel->r_offset - start_offset;
                         switch(type) {
                         case R_390_32:
                             fprintf(outfile, "    *(uint32_t *)(gen_code_ptr + %d) = %s + %d;\n", 
-                                    rel->r_offset - start_offset, name, addend);
+                                    reloc_offset, name, addend);
                             break;
                         case R_390_16:
                             fprintf(outfile, "    *(uint16_t *)(gen_code_ptr + %d) = %s + %d;\n", 
-                                    rel->r_offset - start_offset, name, addend);
+                                    reloc_offset, name, addend);
                             break;
                         case R_390_8:
                             fprintf(outfile, "    *(uint8_t *)(gen_code_ptr + %d) = %s + %d;\n", 
-                                    rel->r_offset - start_offset, name, addend);
+                                    reloc_offset, name, addend);
                             break;
                         default:
                             error("unsupported s390 relocation (%d)", type);
@@ -1913,17 +2096,19 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
                 for (i = 0, rel = relocs; i < nb_relocs; i++, rel++) {
                    if (rel->r_offset >= start_offset && rel->r_offset < start_offset + copy_size) {
                        int type;
+                        long reloc_offset;
 
                        type = ELF64_R_TYPE(rel->r_info);
                        sym_name = strtab + symtab[ELF64_R_SYM(rel->r_info)].st_name;
+                        reloc_offset = rel->r_offset - start_offset;
                        switch (type) {
                        case R_ALPHA_GPDISP:
                            /* The gp is just 32 bit, and never changes, so it's easiest to emit it
                               as an immediate instead of constructing it from the pv or ra.  */
                            fprintf(outfile, "    immediate_ldah(gen_code_ptr + %ld, gp);\n",
-                                   rel->r_offset - start_offset);
+                                   reloc_offset);
                            fprintf(outfile, "    immediate_lda(gen_code_ptr + %ld, gp);\n",
-                                   rel->r_offset - start_offset + rel->r_addend);
+                                   reloc_offset + (int)rel->r_addend);
                            break;
                        case R_ALPHA_LITUSE:
                            /* jsr to literal hint. Could be used to optimize to bsr. Ignore for
@@ -1943,18 +2128,18 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
                               special treatment.  */
                            if (strstart(sym_name, "__op_param", &p))
                                fprintf(outfile, "    immediate_ldah(gen_code_ptr + %ld, param%s);\n",
-                                       rel->r_offset - start_offset, p);
+                                       reloc_offset, p);
                            break;
                        case R_ALPHA_GPRELLOW:
                            if (strstart(sym_name, "__op_param", &p))
                                fprintf(outfile, "    immediate_lda(gen_code_ptr + %ld, param%s);\n",
-                                       rel->r_offset - start_offset, p);
+                                       reloc_offset, p);
                            break;
                        case R_ALPHA_BRSGP:
                            /* PC-relative jump. Tweak offset to skip the two instructions that try to
                               set up the gp from the pv.  */
                            fprintf(outfile, "    fix_bsr(gen_code_ptr + %ld, (uint8_t *) &%s - (gen_code_ptr + %ld + 4) + 8);\n",
-                                   rel->r_offset - start_offset, sym_name, rel->r_offset - start_offset);
+                                   reloc_offset, sym_name, reloc_offset);
                            break;
                        default:
                            error("unsupported Alpha relocation (%d)", type);
@@ -1964,31 +2149,85 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
             }
 #elif defined(HOST_IA64)
             {
+               unsigned long sym_idx;
+               long code_offset;
                 char name[256];
                 int type;
-                int addend;
+                long addend;
+
                 for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
-                    if (rel->r_offset >= start_offset && rel->r_offset < start_offset + copy_size) {
-                        sym_name = strtab + symtab[ELF64_R_SYM(rel->r_info)].st_name;
-                        get_reloc_expr(name, sizeof(name), sym_name);
-                        type = ELF64_R_TYPE(rel->r_info);
-                        addend = rel->r_addend;
-                        switch(type) {
-                       case R_IA64_LTOFF22:
-                           error("must implemnt R_IA64_LTOFF22 relocation");
-                       case R_IA64_PCREL21B:
-                           error("must implemnt R_IA64_PCREL21B relocation");
-                        default:
-                            error("unsupported ia64 relocation (%d)", type);
-                        }
-                    }
+                   sym_idx = ELF64_R_SYM(rel->r_info);
+                    if (rel->r_offset < start_offset
+                       || rel->r_offset >= start_offset + copy_size)
+                       continue;
+                   sym_name = (strtab + symtab[sym_idx].st_name);
+                   code_offset = rel->r_offset - start_offset;
+                   if (strstart(sym_name, "__op_jmp", &p)) {
+                       int n;
+                       n = strtol(p, NULL, 10);
+                       /* __op_jmp relocations are done at
+                          runtime to do translated block
+                          chaining: the offset of the instruction
+                          needs to be stored */
+                       fprintf(outfile, "    jmp_offsets[%d] ="
+                               "%ld + (gen_code_ptr - gen_code_buf);\n",
+                               n, code_offset);
+                       continue;
+                   }
+                   get_reloc_expr(name, sizeof(name), sym_name);
+                   type = ELF64_R_TYPE(rel->r_info);
+                   addend = rel->r_addend;
+                   switch(type) {
+                     case R_IA64_IMM64:
+                         fprintf(outfile,
+                                 "    ia64_imm64(gen_code_ptr + %ld, "
+                                 "%s + %ld);\n",
+                                 code_offset, name, addend);
+                         break;
+                     case R_IA64_LTOFF22X:
+                     case R_IA64_LTOFF22:
+                         fprintf(outfile, "    IA64_LTOFF(gen_code_ptr + %ld,"
+                                 " %s + %ld, %d);\n",
+                                 code_offset, name, addend,
+                                 (type == R_IA64_LTOFF22X));
+                         break;
+                     case R_IA64_LDXMOV:
+                         fprintf(outfile,
+                                 "    ia64_ldxmov(gen_code_ptr + %ld,"
+                                 " %s + %ld);\n", code_offset, name, addend);
+                         break;
+
+                     case R_IA64_PCREL21B:
+                         if (strstart(sym_name, "__op_gen_label", NULL)) {
+                             fprintf(outfile,
+                                     "    ia64_imm21b(gen_code_ptr + %ld,"
+                                     " (long) (%s + %ld -\n\t\t"
+                                     "((long) gen_code_ptr + %ld)) >> 4);\n",
+                                     code_offset, name, addend,
+                                     code_offset & ~0xfUL);
+                         } else {
+                             fprintf(outfile,
+                                     "    IA64_PLT(gen_code_ptr + %ld, "
+                                     "%d);\t/* %s + %ld */\n",
+                                     code_offset,
+                                     get_plt_index(sym_name, addend),
+                                     sym_name, addend);
+                         }
+                         break;
+                     default:
+                         error("unsupported ia64 relocation (0x%x)",
+                               type);
+                   }
                 }
+               fprintf(outfile, "    ia64_nop_b(gen_code_ptr + %d);\n",
+                       copy_size - 16 + 2);
             }
 #elif defined(HOST_SPARC)
             {
                 char name[256];
                 int type;
                 int addend;
+                int reloc_offset;
                 for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
                     if (rel->r_offset >= start_offset &&
                        rel->r_offset < start_offset + copy_size) {
@@ -1996,10 +2235,11 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
                         get_reloc_expr(name, sizeof(name), sym_name);
                         type = ELF32_R_TYPE(rel->r_info);
                         addend = rel->r_addend;
+                        reloc_offset = rel->r_offset - start_offset;
                         switch(type) {
                         case R_SPARC_32:
                             fprintf(outfile, "    *(uint32_t *)(gen_code_ptr + %d) = %s + %d;\n", 
-                                    rel->r_offset - start_offset, name, addend);
+                                    reloc_offset, name, addend);
                            break;
                        case R_SPARC_HI22:
                             fprintf(outfile,
@@ -2007,9 +2247,7 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
                                    "((*(uint32_t *)(gen_code_ptr + %d)) "
                                    " & ~0x3fffff) "
                                    " | (((%s + %d) >> 10) & 0x3fffff);\n",
-                                    rel->r_offset - start_offset,
-                                   rel->r_offset - start_offset,
-                                   name, addend);
+                                    reloc_offset, reloc_offset, name, addend);
                            break;
                        case R_SPARC_LO10:
                             fprintf(outfile,
@@ -2017,9 +2255,7 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
                                    "((*(uint32_t *)(gen_code_ptr + %d)) "
                                    " & ~0x3ff) "
                                    " | ((%s + %d) & 0x3ff);\n",
-                                    rel->r_offset - start_offset,
-                                   rel->r_offset - start_offset,
-                                   name, addend);
+                                    reloc_offset, reloc_offset, name, addend);
                            break;
                        case R_SPARC_WDISP30:
                            fprintf(outfile,
@@ -2028,11 +2264,21 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
                                    " & ~0x3fffffff) "
                                    " | ((((%s + %d) - (long)(gen_code_ptr + %d))>>2) "
                                    "    & 0x3fffffff);\n",
-                                   rel->r_offset - start_offset,
-                                   rel->r_offset - start_offset,
-                                   name, addend,
-                                   rel->r_offset - start_offset);
+                                   reloc_offset, reloc_offset, name, addend,
+                                   reloc_offset);
                            break;
+                        case R_SPARC_WDISP22:
+                            fprintf(outfile,
+                                    "    *(uint32_t *)(gen_code_ptr + %d) = "
+                                    "((*(uint32_t *)(gen_code_ptr + %d)) "
+                                    " & ~0x3fffff) "
+                                    " | ((((%s + %d) - (long)(gen_code_ptr + %d))>>2) "
+                                    "    & 0x3fffff);\n",
+                                    rel->r_offset - start_offset,
+                                    rel->r_offset - start_offset,
+                                    name, addend,
+                                    rel->r_offset - start_offset);
+                            break;
                         default:
                             error("unsupported sparc relocation (%d)", type);
                         }
@@ -2044,17 +2290,19 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
                 char name[256];
                 int type;
                 int addend;
+                int reloc_offset;
                 for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
                     if (rel->r_offset >= start_offset &&
                        rel->r_offset < start_offset + copy_size) {
                         sym_name = strtab + symtab[ELF64_R_SYM(rel->r_info)].st_name;
                         get_reloc_expr(name, sizeof(name), sym_name);
-                        type = ELF64_R_TYPE(rel->r_info);
+                        type = ELF32_R_TYPE(rel->r_info);
                         addend = rel->r_addend;
+                        reloc_offset = rel->r_offset - start_offset;
                         switch(type) {
                         case R_SPARC_32:
                             fprintf(outfile, "    *(uint32_t *)(gen_code_ptr + %d) = %s + %d;\n",
-                                    rel->r_offset - start_offset, name, addend);
+                                    reloc_offset, name, addend);
                            break;
                        case R_SPARC_HI22:
                             fprintf(outfile,
@@ -2062,9 +2310,7 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
                                    "((*(uint32_t *)(gen_code_ptr + %d)) "
                                    " & ~0x3fffff) "
                                    " | (((%s + %d) >> 10) & 0x3fffff);\n",
-                                    rel->r_offset - start_offset,
-                                   rel->r_offset - start_offset,
-                                   name, addend);
+                                    reloc_offset, reloc_offset, name, addend);
                            break;
                        case R_SPARC_LO10:
                             fprintf(outfile,
@@ -2072,9 +2318,16 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
                                    "((*(uint32_t *)(gen_code_ptr + %d)) "
                                    " & ~0x3ff) "
                                    " | ((%s + %d) & 0x3ff);\n",
-                                    rel->r_offset - start_offset,
-                                   rel->r_offset - start_offset,
-                                   name, addend);
+                                    reloc_offset, reloc_offset, name, addend);
+                           break;
+                        case R_SPARC_OLO10:
+                            addend += ELF64_R_TYPE_DATA (rel->r_info);
+                            fprintf(outfile,
+                                   "    *(uint32_t *)(gen_code_ptr + %d) = "
+                                   "((*(uint32_t *)(gen_code_ptr + %d)) "
+                                   " & ~0x3ff) "
+                                   " | ((%s + %d) & 0x3ff);\n",
+                                    reloc_offset, reloc_offset, name, addend);
                            break;
                        case R_SPARC_WDISP30:
                            fprintf(outfile,
@@ -2083,13 +2336,48 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
                                    " & ~0x3fffffff) "
                                    " | ((((%s + %d) - (long)(gen_code_ptr + %d))>>2) "
                                    "    & 0x3fffffff);\n",
-                                   rel->r_offset - start_offset,
-                                   rel->r_offset - start_offset,
-                                   name, addend,
-                                   rel->r_offset - start_offset);
+                                   reloc_offset, reloc_offset, name, addend,
+                                   reloc_offset);
+                           break;
+                        case R_SPARC_WDISP22:
+                            fprintf(outfile,
+                                    "    *(uint32_t *)(gen_code_ptr + %d) = "
+                                    "((*(uint32_t *)(gen_code_ptr + %d)) "
+                                    " & ~0x3fffff) "
+                                    " | ((((%s + %d) - (long)(gen_code_ptr + %d))>>2) "
+                                    "    & 0x3fffff);\n",
+                                    reloc_offset, reloc_offset, name, addend,
+                                   reloc_offset);
+                            break;
+                        case R_SPARC_HH22:
+                            fprintf(outfile,
+                                   "    *(uint32_t *)(gen_code_ptr + %d) = "
+                                   "((*(uint32_t *)(gen_code_ptr + %d)) "
+                                   " & ~0x00000000) "
+                                   " | (((%s + %d) >> 42) & 0x00000000);\n",
+                                    reloc_offset, reloc_offset, name, addend);
+                             break;
+
+                       case R_SPARC_LM22:
+                            fprintf(outfile,
+                                   "    *(uint32_t *)(gen_code_ptr + %d) = "
+                                   "((*(uint32_t *)(gen_code_ptr + %d)) "
+                                   " & ~0x00000000) "
+                                   " | (((%s + %d) >> 10) & 0x00000000);\n",
+                                    reloc_offset, reloc_offset, name, addend);
                            break;
+
+                       case R_SPARC_HM10:
+                            fprintf(outfile,
+                                   "    *(uint32_t *)(gen_code_ptr + %d) = "
+                                   "((*(uint32_t *)(gen_code_ptr + %d)) "
+                                   " & ~0x00000000) "
+                                   " | ((((%s + %d) >> 32 & 0x3ff)) & 0x00000000);\n",
+                                    reloc_offset, reloc_offset, name, addend);
+                           break;
+
                         default:
-                           error("unsupported sparc64 relocation (%d)", type);
+                           error("unsupported sparc64 relocation (%d) for symbol %s", type, name);
                         }
                     }
                 }
@@ -2099,7 +2387,38 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
                 char name[256];
                 int type;
                 int addend;
-
+                int reloc_offset;
+                uint32_t insn;
+
+                insn = get32((uint32_t *)(p_start + 4));
+                /* If prologue ends in sub sp, sp, #const then assume
+                   op has a stack frame and needs the frame pointer.  */
+                if ((insn & 0xffffff00) == 0xe24dd000) {
+                    int i;
+                    uint32_t opcode;
+                    opcode = 0xe28db000; /* add fp, sp, #0.  */
+#if 0
+/* ??? Need to undo the extra stack adjustment at the end of the op.
+   For now just leave the stack misaligned and hope it doesn't break anything
+   too important.  */
+                    if ((insn & 4) != 0) {
+                        /* Preserve doubleword stack alignment.  */
+                        fprintf(outfile,
+                                "    *(uint32_t *)(gen_code_ptr + 4)= 0x%x;\n",
+                                insn + 4);
+                        opcode -= 4;
+                    }
+#endif
+                    insn = get32((uint32_t *)(p_start - 4));
+                    /* Calculate the size of the saved registers,
+                       excluding pc.  */
+                    for (i = 0; i < 15; i++) {
+                        if (insn & (1 << i))
+                            opcode += 4;
+                    }
+                    fprintf(outfile,
+                            "    *(uint32_t *)gen_code_ptr = 0x%x;\n", opcode);
+                }
                 arm_emit_ldr_info(name, start_offset, outfile, p_start, p_end,
                                   relocs, nb_relocs);
 
@@ -2113,14 +2432,17 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
                     get_reloc_expr(name, sizeof(name), sym_name);
                     type = ELF32_R_TYPE(rel->r_info);
                     addend = get32((uint32_t *)(text + rel->r_offset));
+                    reloc_offset = rel->r_offset - start_offset;
                     switch(type) {
                     case R_ARM_ABS32:
                         fprintf(outfile, "    *(uint32_t *)(gen_code_ptr + %d) = %s + %d;\n", 
-                                rel->r_offset - start_offset, name, addend);
+                                reloc_offset, name, addend);
                         break;
                     case R_ARM_PC24:
+                    case R_ARM_JUMP24:
+                    case R_ARM_CALL:
                         fprintf(outfile, "    arm_reloc_pc24((uint32_t *)(gen_code_ptr + %d), 0x%x, %s);\n", 
-                                rel->r_offset - start_offset, addend, name);
+                                reloc_offset, addend, name);
                         break;
                     default:
                         error("unsupported arm relocation (%d)", type);
@@ -2133,6 +2455,7 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
                 char name[256];
                 int type;
                 int addend;
+                int reloc_offset;
                Elf32_Sym *sym;
                 for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
                 if (rel->r_offset >= start_offset &&
@@ -2142,16 +2465,17 @@ void gen_code(const char *name, host_ulong offset, host_ulong size,
                     get_reloc_expr(name, sizeof(name), sym_name);
                     type = ELF32_R_TYPE(rel->r_info);
                     addend = get32((uint32_t *)(text + rel->r_offset)) + rel->r_addend;
+                    reloc_offset = rel->r_offset - start_offset;
                     switch(type) {
                     case R_68K_32:
                        fprintf(outfile, "    /* R_68K_32 RELOC, offset %x */\n", rel->r_offset) ;
                         fprintf(outfile, "    *(uint32_t *)(gen_code_ptr + %d) = %s + %#x;\n", 
-                                rel->r_offset - start_offset, name, addend );
+                                reloc_offset, name, addend );
                         break;
                     case R_68K_PC32:
                        fprintf(outfile, "    /* R_68K_PC32 RELOC, offset %x */\n", rel->r_offset);
                         fprintf(outfile, "    *(uint32_t *)(gen_code_ptr + %d) = %s - (long)(gen_code_ptr + %#x) + %#x;\n", 
-                                rel->r_offset - start_offset, name, rel->r_offset - start_offset, /*sym->st_value+*/ addend);
+                                reloc_offset, name, reloc_offset, /*sym->st_value+*/ addend);
                         break;
                     default:
                         error("unsupported m68k relocation (%d)", type);
@@ -2221,6 +2545,28 @@ int gen_file(FILE *outfile, int out_type)
         
     } else {
         /* generate big code generation switch */
+
+#ifdef HOST_ARM
+        /* We need to know the size of all the ops so we can figure out when
+           to emit constant pools.  This must be consistent with opc.h.  */
+fprintf(outfile,
+"static const uint32_t arm_opc_size[] = {\n"
+"  0,\n" /* end */
+"  0,\n" /* nop */
+"  0,\n" /* nop1 */
+"  0,\n" /* nop2 */
+"  0,\n"); /* nop3 */
+        for(i = 0, sym = symtab; i < nb_syms; i++, sym++) {
+            const char *name;
+            name = get_sym_name(sym);
+            if (strstart(name, OP_PREFIX, NULL)) {
+                fprintf(outfile, "  %d,\n", sym->st_size);
+            }
+       }
+fprintf(outfile,
+"};\n");
+#endif
+
 fprintf(outfile,
 "int dyngen_code(uint8_t *gen_code_buf,\n"
 "                uint16_t *label_offsets, uint16_t *jmp_offsets,\n"
@@ -2231,10 +2577,93 @@ fprintf(outfile,
 "    const uint32_t *opparam_ptr;\n");
 
 #ifdef HOST_ARM
+/* Arm is tricky because it uses constant pools for loading immediate values.
+   We assume (and require) each function is code followed by a constant pool.
+   All the ops are small so this should be ok.  For each op we figure
+   out how much "spare" range we have in the load instructions.  This allows
+   us to insert subsequent ops in between the op and the constant pool,
+   eliminating the neeed to jump around the pool.
+
+   We currently generate:
+   
+   [ For this example we assume merging would move op1_pool out of range.
+     In practice we should be able to combine many ops before the offset
+     limits are reached. ]
+   op1_code;
+   op2_code;
+   goto op3;
+   op2_pool;
+   op1_pool;
+op3:
+   op3_code;
+   ret;
+   op3_pool;
+
+   Ideally we'd put op1_pool before op2_pool, but that requires two passes.
+ */
 fprintf(outfile,
 "    uint8_t *last_gen_code_ptr = gen_code_buf;\n"
 "    LDREntry *arm_ldr_ptr = arm_ldr_table;\n"
-"    uint32_t *arm_data_ptr = arm_data_table;\n");
+"    uint32_t *arm_data_ptr = arm_data_table + ARM_LDR_TABLE_SIZE;\n"
+/* Initialise the parmissible pool offset to an arbitary large value.  */
+"    uint8_t *arm_pool_ptr = gen_code_buf + 0x1000000;\n");
+#endif
+#ifdef HOST_IA64
+    {
+       long addend, not_first = 0;
+       unsigned long sym_idx;
+       int index, max_index;
+       const char *sym_name;
+       EXE_RELOC *rel;
+
+       max_index = -1;
+       for (i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
+           sym_idx = ELF64_R_SYM(rel->r_info);
+           sym_name = (strtab + symtab[sym_idx].st_name);
+           if (strstart(sym_name, "__op_gen_label", NULL))
+               continue;
+           if (ELF64_R_TYPE(rel->r_info) != R_IA64_PCREL21B)
+               continue;
+
+           addend = rel->r_addend;
+           index = get_plt_index(sym_name, addend);
+           if (index <= max_index)
+               continue;
+           max_index = index;
+           fprintf(outfile, "    extern void %s(void);\n", sym_name);
+       }
+
+       fprintf(outfile,
+               "    struct ia64_fixup *plt_fixes = NULL, "
+               "*ltoff_fixes = NULL;\n"
+               "    static long plt_target[] = {\n\t");
+
+       max_index = -1;
+       for (i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
+           sym_idx = ELF64_R_SYM(rel->r_info);
+           sym_name = (strtab + symtab[sym_idx].st_name);
+           if (strstart(sym_name, "__op_gen_label", NULL))
+               continue;
+           if (ELF64_R_TYPE(rel->r_info) != R_IA64_PCREL21B)
+               continue;
+
+           addend = rel->r_addend;
+           index = get_plt_index(sym_name, addend);
+           if (index <= max_index)
+               continue;
+           max_index = index;
+
+           if (not_first)
+               fprintf(outfile, ",\n\t");
+           not_first = 1;
+           if (addend)
+               fprintf(outfile, "(long) &%s + %ld", sym_name, addend);
+           else
+               fprintf(outfile, "(long) &%s", sym_name);
+       }
+       fprintf(outfile, "\n    };\n"
+           "    unsigned int plt_offset[%u] = { 0 };\n", max_index + 1);
+    }
 #endif
 
 fprintf(outfile,
@@ -2246,9 +2675,23 @@ fprintf(outfile,
        /* Generate prologue, if needed. */ 
 
 fprintf(outfile,
-"    for(;;) {\n"
-"        switch(*opc_ptr++) {\n"
-);
+"    for(;;) {\n");
+
+#ifdef HOST_ARM
+/* Generate constant pool if needed */
+fprintf(outfile,
+"            if (gen_code_ptr + arm_opc_size[*opc_ptr] >= arm_pool_ptr) {\n"
+"                gen_code_ptr = arm_flush_ldr(gen_code_ptr, arm_ldr_table, "
+"arm_ldr_ptr, arm_data_ptr, arm_data_table + ARM_LDR_TABLE_SIZE, 1);\n"
+"                last_gen_code_ptr = gen_code_ptr;\n"
+"                arm_ldr_ptr = arm_ldr_table;\n"
+"                arm_data_ptr = arm_data_table + ARM_LDR_TABLE_SIZE;\n"
+"                arm_pool_ptr = gen_code_ptr + 0x1000000;\n"
+"            }\n");
+#endif
+
+fprintf(outfile,
+"        switch(*opc_ptr++) {\n");
 
         for(i = 0, sym = symtab; i < nb_syms; i++, sym++) {
             const char *name;
@@ -2282,26 +2725,27 @@ fprintf(outfile,
 "            goto the_end;\n"
 "        }\n");
 
-#ifdef HOST_ARM
-/* generate constant table if needed */
-fprintf(outfile,
-"        if ((gen_code_ptr - last_gen_code_ptr) >= (MAX_FRAG_SIZE - MAX_OP_SIZE)) {\n"
-"            gen_code_ptr = arm_flush_ldr(gen_code_ptr, arm_ldr_table, arm_ldr_ptr, arm_data_table, arm_data_ptr, 1);\n"
-"            last_gen_code_ptr = gen_code_ptr;\n"
-"            arm_ldr_ptr = arm_ldr_table;\n"
-"            arm_data_ptr = arm_data_table;\n"
-"        }\n");         
-#endif
-
 
 fprintf(outfile,
 "    }\n"
 " the_end:\n"
 );
+#ifdef HOST_IA64
+    fprintf(outfile,
+           "    {\n"
+           "      extern char code_gen_buffer[];\n"
+           "      ia64_apply_fixes(&gen_code_ptr, ltoff_fixes, "
+           "(uint64_t) code_gen_buffer + 2*(1<<20), plt_fixes,\n\t\t\t"
+           "sizeof(plt_target)/sizeof(plt_target[0]),\n\t\t\t"
+           "plt_target, plt_offset);\n    }\n");
+#endif
 
 /* generate some code patching */ 
 #ifdef HOST_ARM
-fprintf(outfile, "gen_code_ptr = arm_flush_ldr(gen_code_ptr, arm_ldr_table, arm_ldr_ptr, arm_data_table, arm_data_ptr, 0);\n");
+fprintf(outfile,
+"if (arm_data_ptr != arm_data_table + ARM_LDR_TABLE_SIZE)\n"
+"    gen_code_ptr = arm_flush_ldr(gen_code_ptr, arm_ldr_table, "
+"arm_ldr_ptr, arm_data_ptr, arm_data_table + ARM_LDR_TABLE_SIZE, 0);\n");
 #endif
     /* flush instruction cache */
     fprintf(outfile, "flush_icache_range((unsigned long)gen_code_buf, (unsigned long)gen_code_ptr);\n");