Commit 46152182100e68f7f8aa4954af1bf91160bb3d15
1 parent
f3a9676a
Rewrite Arm host support.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2071 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
6 changed files
with
295 additions
and
78 deletions
arm.ld
| @@ -53,6 +53,10 @@ SECTIONS | @@ -53,6 +53,10 @@ SECTIONS | ||
| 53 | .fini : { *(.fini) } =0x47ff041f | 53 | .fini : { *(.fini) } =0x47ff041f |
| 54 | .rodata : { *(.rodata) *(.gnu.linkonce.r*) } | 54 | .rodata : { *(.rodata) *(.gnu.linkonce.r*) } |
| 55 | .rodata1 : { *(.rodata1) } | 55 | .rodata1 : { *(.rodata1) } |
| 56 | + .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } | ||
| 57 | + __exidx_start = .; | ||
| 58 | + .ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } | ||
| 59 | + __exidx_end = .; | ||
| 56 | .reginfo : { *(.reginfo) } | 60 | .reginfo : { *(.reginfo) } |
| 57 | /* Adjust the address for the data segment. We want to adjust up to | 61 | /* Adjust the address for the data segment. We want to adjust up to |
| 58 | the same address within the page on the next page up. */ | 62 | the same address within the page on the next page up. */ |
| @@ -63,7 +67,28 @@ SECTIONS | @@ -63,7 +67,28 @@ SECTIONS | ||
| 63 | *(.gnu.linkonce.d*) | 67 | *(.gnu.linkonce.d*) |
| 64 | CONSTRUCTORS | 68 | CONSTRUCTORS |
| 65 | } | 69 | } |
| 70 | + .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } | ||
| 66 | .data1 : { *(.data1) } | 71 | .data1 : { *(.data1) } |
| 72 | + .preinit_array : | ||
| 73 | + { | ||
| 74 | + PROVIDE_HIDDEN (__preinit_array_start = .); | ||
| 75 | + KEEP (*(.preinit_array)) | ||
| 76 | + PROVIDE_HIDDEN (__preinit_array_end = .); | ||
| 77 | + } | ||
| 78 | + .init_array : | ||
| 79 | + { | ||
| 80 | + PROVIDE_HIDDEN (__init_array_start = .); | ||
| 81 | + KEEP (*(SORT(.init_array.*))) | ||
| 82 | + KEEP (*(.init_array)) | ||
| 83 | + PROVIDE_HIDDEN (__init_array_end = .); | ||
| 84 | + } | ||
| 85 | + .fini_array : | ||
| 86 | + { | ||
| 87 | + PROVIDE_HIDDEN (__fini_array_start = .); | ||
| 88 | + KEEP (*(.fini_array)) | ||
| 89 | + KEEP (*(SORT(.fini_array.*))) | ||
| 90 | + PROVIDE_HIDDEN (__fini_array_end = .); | ||
| 91 | + } | ||
| 67 | .ctors : | 92 | .ctors : |
| 68 | { | 93 | { |
| 69 | *(.ctors) | 94 | *(.ctors) |
cpu-all.h
| @@ -992,6 +992,15 @@ static inline int64_t cpu_get_real_ticks (void) | @@ -992,6 +992,15 @@ static inline int64_t cpu_get_real_ticks (void) | ||
| 992 | return rval.i64; | 992 | return rval.i64; |
| 993 | #endif | 993 | #endif |
| 994 | } | 994 | } |
| 995 | +#else | ||
| 996 | +/* The host CPU doesn't have an easily accessible cycle counter. | ||
| 997 | + Just return a monotonically increasing vlue. This will be totally wrong, | ||
| 998 | + but hopefully better than nothing. */ | ||
| 999 | +static inline int64_t cpu_get_real_ticks (void) | ||
| 1000 | +{ | ||
| 1001 | + static int64_t ticks = 0; | ||
| 1002 | + return ticks++; | ||
| 1003 | +} | ||
| 995 | #endif | 1004 | #endif |
| 996 | 1005 | ||
| 997 | /* profiling */ | 1006 | /* profiling */ |
disas.c
| @@ -271,11 +271,9 @@ void disas(FILE *out, void *code, unsigned long size) | @@ -271,11 +271,9 @@ void disas(FILE *out, void *code, unsigned long size) | ||
| 271 | for (pc = (unsigned long)code; pc < (unsigned long)code + size; pc += count) { | 271 | for (pc = (unsigned long)code; pc < (unsigned long)code + size; pc += count) { |
| 272 | fprintf(out, "0x%08lx: ", pc); | 272 | fprintf(out, "0x%08lx: ", pc); |
| 273 | #ifdef __arm__ | 273 | #ifdef __arm__ |
| 274 | - /* since data are included in the code, it is better to | 274 | + /* since data is included in the code, it is better to |
| 275 | display code data too */ | 275 | display code data too */ |
| 276 | - if (is_host) { | ||
| 277 | - fprintf(out, "%08x ", (int)bfd_getl32((const bfd_byte *)pc)); | ||
| 278 | - } | 276 | + fprintf(out, "%08x ", (int)bfd_getl32((const bfd_byte *)pc)); |
| 279 | #endif | 277 | #endif |
| 280 | count = print_insn(pc, &disasm_info); | 278 | count = print_insn(pc, &disasm_info); |
| 281 | fprintf(out, "\n"); | 279 | fprintf(out, "\n"); |
dyngen.c
| @@ -1255,90 +1255,149 @@ int arm_emit_ldr_info(const char *name, unsigned long start_offset, | @@ -1255,90 +1255,149 @@ int arm_emit_ldr_info(const char *name, unsigned long start_offset, | ||
| 1255 | { | 1255 | { |
| 1256 | uint8_t *p; | 1256 | uint8_t *p; |
| 1257 | uint32_t insn; | 1257 | uint32_t insn; |
| 1258 | - int offset, min_offset, pc_offset, data_size; | 1258 | + int offset, min_offset, pc_offset, data_size, spare, max_pool; |
| 1259 | uint8_t data_allocated[1024]; | 1259 | uint8_t data_allocated[1024]; |
| 1260 | unsigned int data_index; | 1260 | unsigned int data_index; |
| 1261 | + int type; | ||
| 1261 | 1262 | ||
| 1262 | memset(data_allocated, 0, sizeof(data_allocated)); | 1263 | memset(data_allocated, 0, sizeof(data_allocated)); |
| 1263 | 1264 | ||
| 1264 | p = p_start; | 1265 | p = p_start; |
| 1265 | min_offset = p_end - p_start; | 1266 | min_offset = p_end - p_start; |
| 1267 | + spare = 0x7fffffff; | ||
| 1266 | while (p < p_start + min_offset) { | 1268 | while (p < p_start + min_offset) { |
| 1267 | insn = get32((uint32_t *)p); | 1269 | insn = get32((uint32_t *)p); |
| 1270 | + /* TODO: Armv5e ldrd. */ | ||
| 1271 | + /* TODO: VFP load. */ | ||
| 1268 | if ((insn & 0x0d5f0000) == 0x051f0000) { | 1272 | if ((insn & 0x0d5f0000) == 0x051f0000) { |
| 1269 | /* ldr reg, [pc, #im] */ | 1273 | /* ldr reg, [pc, #im] */ |
| 1270 | offset = insn & 0xfff; | 1274 | offset = insn & 0xfff; |
| 1271 | if (!(insn & 0x00800000)) | 1275 | if (!(insn & 0x00800000)) |
| 1272 | - offset = -offset; | 1276 | + offset = -offset; |
| 1277 | + max_pool = 4096; | ||
| 1278 | + type = 0; | ||
| 1279 | + } else if ((insn & 0x0e5f0f00) == 0x0c1f0100) { | ||
| 1280 | + /* FPA ldf. */ | ||
| 1281 | + offset = (insn & 0xff) << 2; | ||
| 1282 | + if (!(insn & 0x00800000)) | ||
| 1283 | + offset = -offset; | ||
| 1284 | + max_pool = 1024; | ||
| 1285 | + type = 1; | ||
| 1286 | + } else if ((insn & 0x0fff0000) == 0x028f0000) { | ||
| 1287 | + /* Some gcc load a doubleword immediate with | ||
| 1288 | + add regN, pc, #imm | ||
| 1289 | + ldmia regN, {regN, regM} | ||
| 1290 | + Hope and pray the compiler never generates somethin like | ||
| 1291 | + add reg, pc, #imm1; ldr reg, [reg, #-imm2]; */ | ||
| 1292 | + int r; | ||
| 1293 | + | ||
| 1294 | + r = (insn & 0xf00) >> 7; | ||
| 1295 | + offset = ((insn & 0xff) >> r) | ((insn & 0xff) << (32 - r)); | ||
| 1296 | + max_pool = 1024; | ||
| 1297 | + type = 2; | ||
| 1298 | + } else { | ||
| 1299 | + max_pool = 0; | ||
| 1300 | + type = -1; | ||
| 1301 | + } | ||
| 1302 | + if (type >= 0) { | ||
| 1303 | + /* PC-relative load needs fixing up. */ | ||
| 1304 | + if (spare > max_pool - offset) | ||
| 1305 | + spare = max_pool - offset; | ||
| 1273 | if ((offset & 3) !=0) | 1306 | if ((offset & 3) !=0) |
| 1274 | - error("%s:%04x: ldr pc offset must be 32 bit aligned", | 1307 | + error("%s:%04x: pc offset must be 32 bit aligned", |
| 1308 | + name, start_offset + p - p_start); | ||
| 1309 | + if (offset < 0) | ||
| 1310 | + error("%s:%04x: Embedded literal value", | ||
| 1275 | name, start_offset + p - p_start); | 1311 | name, start_offset + p - p_start); |
| 1276 | pc_offset = p - p_start + offset + 8; | 1312 | pc_offset = p - p_start + offset + 8; |
| 1277 | if (pc_offset <= (p - p_start) || | 1313 | if (pc_offset <= (p - p_start) || |
| 1278 | pc_offset >= (p_end - p_start)) | 1314 | pc_offset >= (p_end - p_start)) |
| 1279 | - error("%s:%04x: ldr pc offset must point inside the function code", | 1315 | + error("%s:%04x: pc offset must point inside the function code", |
| 1280 | name, start_offset + p - p_start); | 1316 | name, start_offset + p - p_start); |
| 1281 | if (pc_offset < min_offset) | 1317 | if (pc_offset < min_offset) |
| 1282 | min_offset = pc_offset; | 1318 | min_offset = pc_offset; |
| 1283 | if (outfile) { | 1319 | if (outfile) { |
| 1284 | - /* ldr position */ | 1320 | + /* The intruction position */ |
| 1285 | fprintf(outfile, " arm_ldr_ptr->ptr = gen_code_ptr + %d;\n", | 1321 | fprintf(outfile, " arm_ldr_ptr->ptr = gen_code_ptr + %d;\n", |
| 1286 | p - p_start); | 1322 | p - p_start); |
| 1287 | - /* ldr data index */ | ||
| 1288 | - data_index = ((p_end - p_start) - pc_offset - 4) >> 2; | ||
| 1289 | - fprintf(outfile, " arm_ldr_ptr->data_ptr = arm_data_ptr + %d;\n", | 1323 | + /* The position of the constant pool data. */ |
| 1324 | + data_index = ((p_end - p_start) - pc_offset) >> 2; | ||
| 1325 | + fprintf(outfile, " arm_ldr_ptr->data_ptr = arm_data_ptr - %d;\n", | ||
| 1290 | data_index); | 1326 | data_index); |
| 1327 | + fprintf(outfile, " arm_ldr_ptr->type = %d;\n", type); | ||
| 1291 | fprintf(outfile, " arm_ldr_ptr++;\n"); | 1328 | fprintf(outfile, " arm_ldr_ptr++;\n"); |
| 1292 | - if (data_index >= sizeof(data_allocated)) | ||
| 1293 | - error("%s: too many data", name); | ||
| 1294 | - if (!data_allocated[data_index]) { | ||
| 1295 | - ELF_RELOC *rel; | ||
| 1296 | - int i, addend, type; | ||
| 1297 | - const char *sym_name, *p; | ||
| 1298 | - char relname[1024]; | ||
| 1299 | - | ||
| 1300 | - data_allocated[data_index] = 1; | ||
| 1301 | - | ||
| 1302 | - /* data value */ | ||
| 1303 | - addend = get32((uint32_t *)(p_start + pc_offset)); | ||
| 1304 | - relname[0] = '\0'; | ||
| 1305 | - for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) { | ||
| 1306 | - if (rel->r_offset == (pc_offset + start_offset)) { | ||
| 1307 | - sym_name = get_rel_sym_name(rel); | ||
| 1308 | - /* the compiler leave some unnecessary references to the code */ | ||
| 1309 | - get_reloc_expr(relname, sizeof(relname), sym_name); | ||
| 1310 | - type = ELF32_R_TYPE(rel->r_info); | ||
| 1311 | - if (type != R_ARM_ABS32) | ||
| 1312 | - error("%s: unsupported data relocation", name); | ||
| 1313 | - break; | ||
| 1314 | - } | ||
| 1315 | - } | ||
| 1316 | - fprintf(outfile, " arm_data_ptr[%d] = 0x%x", | ||
| 1317 | - data_index, addend); | ||
| 1318 | - if (relname[0] != '\0') | ||
| 1319 | - fprintf(outfile, " + %s", relname); | ||
| 1320 | - fprintf(outfile, ";\n"); | ||
| 1321 | - } | ||
| 1322 | } | 1329 | } |
| 1323 | } | 1330 | } |
| 1324 | p += 4; | 1331 | p += 4; |
| 1325 | } | 1332 | } |
| 1333 | + | ||
| 1334 | + /* Copy and relocate the constant pool data. */ | ||
| 1326 | data_size = (p_end - p_start) - min_offset; | 1335 | data_size = (p_end - p_start) - min_offset; |
| 1327 | if (data_size > 0 && outfile) { | 1336 | if (data_size > 0 && outfile) { |
| 1328 | - fprintf(outfile, " arm_data_ptr += %d;\n", data_size >> 2); | 1337 | + spare += min_offset; |
| 1338 | + fprintf(outfile, " arm_data_ptr -= %d;\n", data_size >> 2); | ||
| 1339 | + fprintf(outfile, " arm_pool_ptr -= %d;\n", data_size); | ||
| 1340 | + fprintf(outfile, " if (arm_pool_ptr > gen_code_ptr + %d)\n" | ||
| 1341 | + " arm_pool_ptr = gen_code_ptr + %d;\n", | ||
| 1342 | + spare, spare); | ||
| 1343 | + | ||
| 1344 | + data_index = 0; | ||
| 1345 | + for (pc_offset = min_offset; | ||
| 1346 | + pc_offset < p_end - p_start; | ||
| 1347 | + pc_offset += 4) { | ||
| 1348 | + | ||
| 1349 | + ELF_RELOC *rel; | ||
| 1350 | + int i, addend, type; | ||
| 1351 | + const char *sym_name; | ||
| 1352 | + char relname[1024]; | ||
| 1353 | + | ||
| 1354 | + /* data value */ | ||
| 1355 | + addend = get32((uint32_t *)(p_start + pc_offset)); | ||
| 1356 | + relname[0] = '\0'; | ||
| 1357 | + for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) { | ||
| 1358 | + if (rel->r_offset == (pc_offset + start_offset)) { | ||
| 1359 | + sym_name = get_rel_sym_name(rel); | ||
| 1360 | + /* the compiler leave some unnecessary references to the code */ | ||
| 1361 | + get_reloc_expr(relname, sizeof(relname), sym_name); | ||
| 1362 | + type = ELF32_R_TYPE(rel->r_info); | ||
| 1363 | + if (type != R_ARM_ABS32) | ||
| 1364 | + error("%s: unsupported data relocation", name); | ||
| 1365 | + break; | ||
| 1366 | + } | ||
| 1367 | + } | ||
| 1368 | + fprintf(outfile, " arm_data_ptr[%d] = 0x%x", | ||
| 1369 | + data_index, addend); | ||
| 1370 | + if (relname[0] != '\0') | ||
| 1371 | + fprintf(outfile, " + %s", relname); | ||
| 1372 | + fprintf(outfile, ";\n"); | ||
| 1373 | + | ||
| 1374 | + data_index++; | ||
| 1375 | + } | ||
| 1329 | } | 1376 | } |
| 1330 | 1377 | ||
| 1331 | - /* the last instruction must be a mov pc, lr */ | ||
| 1332 | if (p == p_start) | 1378 | if (p == p_start) |
| 1333 | goto arm_ret_error; | 1379 | goto arm_ret_error; |
| 1334 | p -= 4; | 1380 | p -= 4; |
| 1335 | insn = get32((uint32_t *)p); | 1381 | insn = get32((uint32_t *)p); |
| 1336 | - if ((insn & 0xffff0000) != 0xe91b0000) { | 1382 | + /* The last instruction must be an ldm instruction. There are several |
| 1383 | + forms generated by gcc: | ||
| 1384 | + ldmib sp, {..., pc} (implies a sp adjustment of +4) | ||
| 1385 | + ldmia sp, {..., pc} | ||
| 1386 | + ldmea fp, {..., pc} */ | ||
| 1387 | + if ((insn & 0xffff8000) == 0xe99d8000) { | ||
| 1388 | + if (outfile) { | ||
| 1389 | + fprintf(outfile, | ||
| 1390 | + " *(uint32_t *)(gen_code_ptr + %d) = 0xe28dd004;\n", | ||
| 1391 | + p - p_start); | ||
| 1392 | + } | ||
| 1393 | + p += 4; | ||
| 1394 | + } else if ((insn & 0xffff8000) != 0xe89d8000 | ||
| 1395 | + && (insn & 0xffff8000) != 0xe91b8000) { | ||
| 1337 | arm_ret_error: | 1396 | arm_ret_error: |
| 1338 | if (!outfile) | 1397 | if (!outfile) |
| 1339 | printf("%s: invalid epilog\n", name); | 1398 | printf("%s: invalid epilog\n", name); |
| 1340 | } | 1399 | } |
| 1341 | - return p - p_start; | 1400 | + return p - p_start; |
| 1342 | } | 1401 | } |
| 1343 | #endif | 1402 | #endif |
| 1344 | 1403 | ||
| @@ -1537,6 +1596,8 @@ void gen_code(const char *name, host_ulong offset, host_ulong size, | @@ -1537,6 +1596,8 @@ void gen_code(const char *name, host_ulong offset, host_ulong size, | ||
| 1537 | } | 1596 | } |
| 1538 | #elif defined(HOST_ARM) | 1597 | #elif defined(HOST_ARM) |
| 1539 | { | 1598 | { |
| 1599 | + uint32_t insn; | ||
| 1600 | + | ||
| 1540 | if ((p_end - p_start) <= 16) | 1601 | if ((p_end - p_start) <= 16) |
| 1541 | error("%s: function too small", name); | 1602 | error("%s: function too small", name); |
| 1542 | if (get32((uint32_t *)p_start) != 0xe1a0c00d || | 1603 | if (get32((uint32_t *)p_start) != 0xe1a0c00d || |
| @@ -1545,6 +1606,12 @@ void gen_code(const char *name, host_ulong offset, host_ulong size, | @@ -1545,6 +1606,12 @@ void gen_code(const char *name, host_ulong offset, host_ulong size, | ||
| 1545 | error("%s: invalid prolog", name); | 1606 | error("%s: invalid prolog", name); |
| 1546 | p_start += 12; | 1607 | p_start += 12; |
| 1547 | start_offset += 12; | 1608 | start_offset += 12; |
| 1609 | + insn = get32((uint32_t *)p_start); | ||
| 1610 | + if ((insn & 0xffffff00) == 0xe24dd000) { | ||
| 1611 | + /* Stack adjustment. Assume op uses the frame pointer. */ | ||
| 1612 | + p_start -= 4; | ||
| 1613 | + start_offset -= 4; | ||
| 1614 | + } | ||
| 1548 | copy_size = arm_emit_ldr_info(name, start_offset, NULL, p_start, p_end, | 1615 | copy_size = arm_emit_ldr_info(name, start_offset, NULL, p_start, p_end, |
| 1549 | relocs, nb_relocs); | 1616 | relocs, nb_relocs); |
| 1550 | } | 1617 | } |
| @@ -2282,7 +2349,37 @@ void gen_code(const char *name, host_ulong offset, host_ulong size, | @@ -2282,7 +2349,37 @@ void gen_code(const char *name, host_ulong offset, host_ulong size, | ||
| 2282 | int type; | 2349 | int type; |
| 2283 | int addend; | 2350 | int addend; |
| 2284 | int reloc_offset; | 2351 | int reloc_offset; |
| 2285 | - | 2352 | + uint32_t insn; |
| 2353 | + | ||
| 2354 | + insn = get32((uint32_t *)(p_start + 4)); | ||
| 2355 | + /* If prologue ends in sub sp, sp, #const then assume | ||
| 2356 | + op has a stack frame and needs the frame pointer. */ | ||
| 2357 | + if ((insn & 0xffffff00) == 0xe24dd000) { | ||
| 2358 | + int i; | ||
| 2359 | + uint32_t opcode; | ||
| 2360 | + opcode = 0xe28db000; /* add fp, sp, #0. */ | ||
| 2361 | +#if 0 | ||
| 2362 | +/* ??? Need to undo the extra stack adjustment at the end of the op. | ||
| 2363 | + For now just leave the stack misaligned and hope it doesn't break anything | ||
| 2364 | + too important. */ | ||
| 2365 | + if ((insn & 4) != 0) { | ||
| 2366 | + /* Preserve doubleword stack alignment. */ | ||
| 2367 | + fprintf(outfile, | ||
| 2368 | + " *(uint32_t *)(gen_code_ptr + 4)= 0x%x;\n", | ||
| 2369 | + insn + 4); | ||
| 2370 | + opcode -= 4; | ||
| 2371 | + } | ||
| 2372 | +#endif | ||
| 2373 | + insn = get32((uint32_t *)(p_start - 4)); | ||
| 2374 | + /* Calculate the size of the saved registers, | ||
| 2375 | + excluding pc. */ | ||
| 2376 | + for (i = 0; i < 15; i++) { | ||
| 2377 | + if (insn & (1 << i)) | ||
| 2378 | + opcode += 4; | ||
| 2379 | + } | ||
| 2380 | + fprintf(outfile, | ||
| 2381 | + " *(uint32_t *)gen_code_ptr = 0x%x;\n", opcode); | ||
| 2382 | + } | ||
| 2286 | arm_emit_ldr_info(name, start_offset, outfile, p_start, p_end, | 2383 | arm_emit_ldr_info(name, start_offset, outfile, p_start, p_end, |
| 2287 | relocs, nb_relocs); | 2384 | relocs, nb_relocs); |
| 2288 | 2385 | ||
| @@ -2303,6 +2400,8 @@ void gen_code(const char *name, host_ulong offset, host_ulong size, | @@ -2303,6 +2400,8 @@ void gen_code(const char *name, host_ulong offset, host_ulong size, | ||
| 2303 | reloc_offset, name, addend); | 2400 | reloc_offset, name, addend); |
| 2304 | break; | 2401 | break; |
| 2305 | case R_ARM_PC24: | 2402 | case R_ARM_PC24: |
| 2403 | + case R_ARM_JUMP24: | ||
| 2404 | + case R_ARM_CALL: | ||
| 2306 | fprintf(outfile, " arm_reloc_pc24((uint32_t *)(gen_code_ptr + %d), 0x%x, %s);\n", | 2405 | fprintf(outfile, " arm_reloc_pc24((uint32_t *)(gen_code_ptr + %d), 0x%x, %s);\n", |
| 2307 | reloc_offset, addend, name); | 2406 | reloc_offset, addend, name); |
| 2308 | break; | 2407 | break; |
| @@ -2407,6 +2506,28 @@ int gen_file(FILE *outfile, int out_type) | @@ -2407,6 +2506,28 @@ int gen_file(FILE *outfile, int out_type) | ||
| 2407 | 2506 | ||
| 2408 | } else { | 2507 | } else { |
| 2409 | /* generate big code generation switch */ | 2508 | /* generate big code generation switch */ |
| 2509 | + | ||
| 2510 | +#ifdef HOST_ARM | ||
| 2511 | + /* We need to know the size of all the ops so we can figure out when | ||
| 2512 | + to emit constant pools. This must be consistent with opc.h. */ | ||
| 2513 | +fprintf(outfile, | ||
| 2514 | +"static const uint32_t arm_opc_size[] = {\n" | ||
| 2515 | +" 0,\n" /* end */ | ||
| 2516 | +" 0,\n" /* nop */ | ||
| 2517 | +" 0,\n" /* nop1 */ | ||
| 2518 | +" 0,\n" /* nop2 */ | ||
| 2519 | +" 0,\n"); /* nop3 */ | ||
| 2520 | + for(i = 0, sym = symtab; i < nb_syms; i++, sym++) { | ||
| 2521 | + const char *name; | ||
| 2522 | + name = get_sym_name(sym); | ||
| 2523 | + if (strstart(name, OP_PREFIX, NULL)) { | ||
| 2524 | + fprintf(outfile, " %d,\n", sym->st_size); | ||
| 2525 | + } | ||
| 2526 | + } | ||
| 2527 | +fprintf(outfile, | ||
| 2528 | +"};\n"); | ||
| 2529 | +#endif | ||
| 2530 | + | ||
| 2410 | fprintf(outfile, | 2531 | fprintf(outfile, |
| 2411 | "int dyngen_code(uint8_t *gen_code_buf,\n" | 2532 | "int dyngen_code(uint8_t *gen_code_buf,\n" |
| 2412 | " uint16_t *label_offsets, uint16_t *jmp_offsets,\n" | 2533 | " uint16_t *label_offsets, uint16_t *jmp_offsets,\n" |
| @@ -2417,10 +2538,36 @@ fprintf(outfile, | @@ -2417,10 +2538,36 @@ fprintf(outfile, | ||
| 2417 | " const uint32_t *opparam_ptr;\n"); | 2538 | " const uint32_t *opparam_ptr;\n"); |
| 2418 | 2539 | ||
| 2419 | #ifdef HOST_ARM | 2540 | #ifdef HOST_ARM |
| 2541 | +/* Arm is tricky because it uses constant pools for loading immediate values. | ||
| 2542 | + We assume (and require) each function is code followed by a constant pool. | ||
| 2543 | + All the ops are small so this should be ok. For each op we figure | ||
| 2544 | + out how much "spare" range we have in the load instructions. This allows | ||
| 2545 | + us to insert subsequent ops in between the op and the constant pool, | ||
| 2546 | + eliminating the neeed to jump around the pool. | ||
| 2547 | + | ||
| 2548 | + We currently generate: | ||
| 2549 | + | ||
| 2550 | + [ For this example we assume merging would move op1_pool out of range. | ||
| 2551 | + In practice we should be able to combine many ops before the offset | ||
| 2552 | + limits are reached. ] | ||
| 2553 | + op1_code; | ||
| 2554 | + op2_code; | ||
| 2555 | + goto op3; | ||
| 2556 | + op2_pool; | ||
| 2557 | + op1_pool; | ||
| 2558 | +op3: | ||
| 2559 | + op3_code; | ||
| 2560 | + ret; | ||
| 2561 | + op3_pool; | ||
| 2562 | + | ||
| 2563 | + Ideally we'd put op1_pool before op2_pool, but that requires two passes. | ||
| 2564 | + */ | ||
| 2420 | fprintf(outfile, | 2565 | fprintf(outfile, |
| 2421 | " uint8_t *last_gen_code_ptr = gen_code_buf;\n" | 2566 | " uint8_t *last_gen_code_ptr = gen_code_buf;\n" |
| 2422 | " LDREntry *arm_ldr_ptr = arm_ldr_table;\n" | 2567 | " LDREntry *arm_ldr_ptr = arm_ldr_table;\n" |
| 2423 | -" uint32_t *arm_data_ptr = arm_data_table;\n"); | 2568 | +" uint32_t *arm_data_ptr = arm_data_table + ARM_LDR_TABLE_SIZE;\n" |
| 2569 | +/* Initialise the parmissible pool offset to an arbitary large value. */ | ||
| 2570 | +" uint8_t *arm_pool_ptr = gen_code_buf + 0x1000000;\n"); | ||
| 2424 | #endif | 2571 | #endif |
| 2425 | #ifdef HOST_IA64 | 2572 | #ifdef HOST_IA64 |
| 2426 | { | 2573 | { |
| @@ -2489,9 +2636,23 @@ fprintf(outfile, | @@ -2489,9 +2636,23 @@ fprintf(outfile, | ||
| 2489 | /* Generate prologue, if needed. */ | 2636 | /* Generate prologue, if needed. */ |
| 2490 | 2637 | ||
| 2491 | fprintf(outfile, | 2638 | fprintf(outfile, |
| 2492 | -" for(;;) {\n" | ||
| 2493 | -" switch(*opc_ptr++) {\n" | ||
| 2494 | -); | 2639 | +" for(;;) {\n"); |
| 2640 | + | ||
| 2641 | +#ifdef HOST_ARM | ||
| 2642 | +/* Generate constant pool if needed */ | ||
| 2643 | +fprintf(outfile, | ||
| 2644 | +" if (gen_code_ptr + arm_opc_size[*opc_ptr] >= arm_pool_ptr) {\n" | ||
| 2645 | +" gen_code_ptr = arm_flush_ldr(gen_code_ptr, arm_ldr_table, " | ||
| 2646 | +"arm_ldr_ptr, arm_data_ptr, arm_data_table + ARM_LDR_TABLE_SIZE, 1);\n" | ||
| 2647 | +" last_gen_code_ptr = gen_code_ptr;\n" | ||
| 2648 | +" arm_ldr_ptr = arm_ldr_table;\n" | ||
| 2649 | +" arm_data_ptr = arm_data_table + ARM_LDR_TABLE_SIZE;\n" | ||
| 2650 | +" arm_pool_ptr = gen_code_ptr + 0x1000000;\n" | ||
| 2651 | +" }\n"); | ||
| 2652 | +#endif | ||
| 2653 | + | ||
| 2654 | +fprintf(outfile, | ||
| 2655 | +" switch(*opc_ptr++) {\n"); | ||
| 2495 | 2656 | ||
| 2496 | for(i = 0, sym = symtab; i < nb_syms; i++, sym++) { | 2657 | for(i = 0, sym = symtab; i < nb_syms; i++, sym++) { |
| 2497 | const char *name; | 2658 | const char *name; |
| @@ -2525,17 +2686,6 @@ fprintf(outfile, | @@ -2525,17 +2686,6 @@ fprintf(outfile, | ||
| 2525 | " goto the_end;\n" | 2686 | " goto the_end;\n" |
| 2526 | " }\n"); | 2687 | " }\n"); |
| 2527 | 2688 | ||
| 2528 | -#ifdef HOST_ARM | ||
| 2529 | -/* generate constant table if needed */ | ||
| 2530 | -fprintf(outfile, | ||
| 2531 | -" if ((gen_code_ptr - last_gen_code_ptr) >= (MAX_FRAG_SIZE - MAX_OP_SIZE)) {\n" | ||
| 2532 | -" gen_code_ptr = arm_flush_ldr(gen_code_ptr, arm_ldr_table, arm_ldr_ptr, arm_data_table, arm_data_ptr, 1);\n" | ||
| 2533 | -" last_gen_code_ptr = gen_code_ptr;\n" | ||
| 2534 | -" arm_ldr_ptr = arm_ldr_table;\n" | ||
| 2535 | -" arm_data_ptr = arm_data_table;\n" | ||
| 2536 | -" }\n"); | ||
| 2537 | -#endif | ||
| 2538 | - | ||
| 2539 | 2689 | ||
| 2540 | fprintf(outfile, | 2690 | fprintf(outfile, |
| 2541 | " }\n" | 2691 | " }\n" |
| @@ -2553,7 +2703,10 @@ fprintf(outfile, | @@ -2553,7 +2703,10 @@ fprintf(outfile, | ||
| 2553 | 2703 | ||
| 2554 | /* generate some code patching */ | 2704 | /* generate some code patching */ |
| 2555 | #ifdef HOST_ARM | 2705 | #ifdef HOST_ARM |
| 2556 | -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"); | 2706 | +fprintf(outfile, |
| 2707 | +"if (arm_data_ptr != arm_data_table + ARM_LDR_TABLE_SIZE)\n" | ||
| 2708 | +" gen_code_ptr = arm_flush_ldr(gen_code_ptr, arm_ldr_table, " | ||
| 2709 | +"arm_ldr_ptr, arm_data_ptr, arm_data_table + ARM_LDR_TABLE_SIZE, 0);\n"); | ||
| 2557 | #endif | 2710 | #endif |
| 2558 | /* flush instruction cache */ | 2711 | /* flush instruction cache */ |
| 2559 | fprintf(outfile, "flush_icache_range((unsigned long)gen_code_buf, (unsigned long)gen_code_ptr);\n"); | 2712 | fprintf(outfile, "flush_icache_range((unsigned long)gen_code_buf, (unsigned long)gen_code_ptr);\n"); |
dyngen.h
| @@ -19,7 +19,7 @@ | @@ -19,7 +19,7 @@ | ||
| 19 | */ | 19 | */ |
| 20 | 20 | ||
| 21 | int __op_param1, __op_param2, __op_param3; | 21 | int __op_param1, __op_param2, __op_param3; |
| 22 | -#ifdef __sparc__ | 22 | +#if defined(__sparc__) || defined(__arm__) |
| 23 | void __op_gen_label1(){} | 23 | void __op_gen_label1(){} |
| 24 | void __op_gen_label2(){} | 24 | void __op_gen_label2(){} |
| 25 | void __op_gen_label3(){} | 25 | void __op_gen_label3(){} |
| @@ -145,18 +145,16 @@ void fix_bsr(void *p, int offset) { | @@ -145,18 +145,16 @@ void fix_bsr(void *p, int offset) { | ||
| 145 | 145 | ||
| 146 | #ifdef __arm__ | 146 | #ifdef __arm__ |
| 147 | 147 | ||
| 148 | -#define MAX_OP_SIZE (128 * 4) /* in bytes */ | ||
| 149 | -/* max size of the code that can be generated without calling arm_flush_ldr */ | ||
| 150 | -#define MAX_FRAG_SIZE (1024 * 4) | ||
| 151 | -//#define MAX_FRAG_SIZE (135 * 4) /* for testing */ | 148 | +#define ARM_LDR_TABLE_SIZE 1024 |
| 152 | 149 | ||
| 153 | typedef struct LDREntry { | 150 | typedef struct LDREntry { |
| 154 | uint8_t *ptr; | 151 | uint8_t *ptr; |
| 155 | uint32_t *data_ptr; | 152 | uint32_t *data_ptr; |
| 153 | + unsigned type:2; | ||
| 156 | } LDREntry; | 154 | } LDREntry; |
| 157 | 155 | ||
| 158 | static LDREntry arm_ldr_table[1024]; | 156 | static LDREntry arm_ldr_table[1024]; |
| 159 | -static uint32_t arm_data_table[1024]; | 157 | +static uint32_t arm_data_table[ARM_LDR_TABLE_SIZE]; |
| 160 | 158 | ||
| 161 | extern char exec_loop; | 159 | extern char exec_loop; |
| 162 | 160 | ||
| @@ -175,8 +173,9 @@ static uint8_t *arm_flush_ldr(uint8_t *gen_code_ptr, | @@ -175,8 +173,9 @@ static uint8_t *arm_flush_ldr(uint8_t *gen_code_ptr, | ||
| 175 | int offset, data_size, target; | 173 | int offset, data_size, target; |
| 176 | uint8_t *data_ptr; | 174 | uint8_t *data_ptr; |
| 177 | uint32_t insn; | 175 | uint32_t insn; |
| 176 | + uint32_t mask; | ||
| 178 | 177 | ||
| 179 | - data_size = (uint8_t *)data_end - (uint8_t *)data_start; | 178 | + data_size = (data_end - data_start) << 2; |
| 180 | 179 | ||
| 181 | if (gen_jmp) { | 180 | if (gen_jmp) { |
| 182 | /* generate branch to skip the data */ | 181 | /* generate branch to skip the data */ |
| @@ -198,17 +197,48 @@ static uint8_t *arm_flush_ldr(uint8_t *gen_code_ptr, | @@ -198,17 +197,48 @@ static uint8_t *arm_flush_ldr(uint8_t *gen_code_ptr, | ||
| 198 | offset = ((unsigned long)(le->data_ptr) - (unsigned long)data_start) + | 197 | offset = ((unsigned long)(le->data_ptr) - (unsigned long)data_start) + |
| 199 | (unsigned long)data_ptr - | 198 | (unsigned long)data_ptr - |
| 200 | (unsigned long)ptr - 8; | 199 | (unsigned long)ptr - 8; |
| 201 | - insn = *ptr & ~(0xfff | 0x00800000); | ||
| 202 | if (offset < 0) { | 200 | if (offset < 0) { |
| 203 | - offset = - offset; | ||
| 204 | - } else { | ||
| 205 | - insn |= 0x00800000; | ||
| 206 | - } | ||
| 207 | - if (offset > 0xfff) { | ||
| 208 | - fprintf(stderr, "Error ldr offset\n"); | 201 | + fprintf(stderr, "Negative constant pool offset\n"); |
| 209 | abort(); | 202 | abort(); |
| 210 | } | 203 | } |
| 211 | - insn |= offset; | 204 | + switch (le->type) { |
| 205 | + case 0: /* ldr */ | ||
| 206 | + mask = ~0x00800fff; | ||
| 207 | + if (offset >= 4096) { | ||
| 208 | + fprintf(stderr, "Bad ldr offset\n"); | ||
| 209 | + abort(); | ||
| 210 | + } | ||
| 211 | + break; | ||
| 212 | + case 1: /* ldc */ | ||
| 213 | + mask = ~0x008000ff; | ||
| 214 | + if (offset >= 1024 ) { | ||
| 215 | + fprintf(stderr, "Bad ldc offset\n"); | ||
| 216 | + abort(); | ||
| 217 | + } | ||
| 218 | + break; | ||
| 219 | + case 2: /* add */ | ||
| 220 | + mask = ~0xfff; | ||
| 221 | + if (offset >= 1024 ) { | ||
| 222 | + fprintf(stderr, "Bad add offset\n"); | ||
| 223 | + abort(); | ||
| 224 | + } | ||
| 225 | + break; | ||
| 226 | + default: | ||
| 227 | + fprintf(stderr, "Bad pc relative fixup\n"); | ||
| 228 | + abort(); | ||
| 229 | + } | ||
| 230 | + insn = *ptr & mask; | ||
| 231 | + switch (le->type) { | ||
| 232 | + case 0: /* ldr */ | ||
| 233 | + insn |= offset | 0x00800000; | ||
| 234 | + break; | ||
| 235 | + case 1: /* ldc */ | ||
| 236 | + insn |= (offset >> 2) | 0x00800000; | ||
| 237 | + break; | ||
| 238 | + case 2: /* add */ | ||
| 239 | + insn |= (offset >> 2) | 0xf00; | ||
| 240 | + break; | ||
| 241 | + } | ||
| 212 | *ptr = insn; | 242 | *ptr = insn; |
| 213 | } | 243 | } |
| 214 | return gen_code_ptr; | 244 | return gen_code_ptr; |
elf.h
| @@ -502,6 +502,8 @@ typedef struct { | @@ -502,6 +502,8 @@ typedef struct { | ||
| 502 | #define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ | 502 | #define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ |
| 503 | #define R_ARM_GOT32 26 /* 32 bit GOT entry */ | 503 | #define R_ARM_GOT32 26 /* 32 bit GOT entry */ |
| 504 | #define R_ARM_PLT32 27 /* 32 bit PLT address */ | 504 | #define R_ARM_PLT32 27 /* 32 bit PLT address */ |
| 505 | +#define R_ARM_CALL 28 | ||
| 506 | +#define R_ARM_JUMP24 29 | ||
| 505 | #define R_ARM_GNU_VTENTRY 100 | 507 | #define R_ARM_GNU_VTENTRY 100 |
| 506 | #define R_ARM_GNU_VTINHERIT 101 | 508 | #define R_ARM_GNU_VTINHERIT 101 |
| 507 | #define R_ARM_THM_PC11 102 /* thumb unconditional branch */ | 509 | #define R_ARM_THM_PC11 102 /* thumb unconditional branch */ |