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 | 53 | .fini : { *(.fini) } =0x47ff041f |
| 54 | 54 | .rodata : { *(.rodata) *(.gnu.linkonce.r*) } |
| 55 | 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 | 60 | .reginfo : { *(.reginfo) } |
| 57 | 61 | /* Adjust the address for the data segment. We want to adjust up to |
| 58 | 62 | the same address within the page on the next page up. */ |
| ... | ... | @@ -63,7 +67,28 @@ SECTIONS |
| 63 | 67 | *(.gnu.linkonce.d*) |
| 64 | 68 | CONSTRUCTORS |
| 65 | 69 | } |
| 70 | + .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } | |
| 66 | 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 | 92 | .ctors : |
| 68 | 93 | { |
| 69 | 94 | *(.ctors) | ... | ... |
cpu-all.h
| ... | ... | @@ -992,6 +992,15 @@ static inline int64_t cpu_get_real_ticks (void) |
| 992 | 992 | return rval.i64; |
| 993 | 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 | 1004 | #endif |
| 996 | 1005 | |
| 997 | 1006 | /* profiling */ | ... | ... |
disas.c
| ... | ... | @@ -271,11 +271,9 @@ void disas(FILE *out, void *code, unsigned long size) |
| 271 | 271 | for (pc = (unsigned long)code; pc < (unsigned long)code + size; pc += count) { |
| 272 | 272 | fprintf(out, "0x%08lx: ", pc); |
| 273 | 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 | 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 | 277 | #endif |
| 280 | 278 | count = print_insn(pc, &disasm_info); |
| 281 | 279 | fprintf(out, "\n"); | ... | ... |
dyngen.c
| ... | ... | @@ -1255,90 +1255,149 @@ int arm_emit_ldr_info(const char *name, unsigned long start_offset, |
| 1255 | 1255 | { |
| 1256 | 1256 | uint8_t *p; |
| 1257 | 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 | 1259 | uint8_t data_allocated[1024]; |
| 1260 | 1260 | unsigned int data_index; |
| 1261 | + int type; | |
| 1261 | 1262 | |
| 1262 | 1263 | memset(data_allocated, 0, sizeof(data_allocated)); |
| 1263 | 1264 | |
| 1264 | 1265 | p = p_start; |
| 1265 | 1266 | min_offset = p_end - p_start; |
| 1267 | + spare = 0x7fffffff; | |
| 1266 | 1268 | while (p < p_start + min_offset) { |
| 1267 | 1269 | insn = get32((uint32_t *)p); |
| 1270 | + /* TODO: Armv5e ldrd. */ | |
| 1271 | + /* TODO: VFP load. */ | |
| 1268 | 1272 | if ((insn & 0x0d5f0000) == 0x051f0000) { |
| 1269 | 1273 | /* ldr reg, [pc, #im] */ |
| 1270 | 1274 | offset = insn & 0xfff; |
| 1271 | 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 | 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 | 1311 | name, start_offset + p - p_start); |
| 1276 | 1312 | pc_offset = p - p_start + offset + 8; |
| 1277 | 1313 | if (pc_offset <= (p - p_start) || |
| 1278 | 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 | 1316 | name, start_offset + p - p_start); |
| 1281 | 1317 | if (pc_offset < min_offset) |
| 1282 | 1318 | min_offset = pc_offset; |
| 1283 | 1319 | if (outfile) { |
| 1284 | - /* ldr position */ | |
| 1320 | + /* The intruction position */ | |
| 1285 | 1321 | fprintf(outfile, " arm_ldr_ptr->ptr = gen_code_ptr + %d;\n", |
| 1286 | 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 | 1326 | data_index); |
| 1327 | + fprintf(outfile, " arm_ldr_ptr->type = %d;\n", type); | |
| 1291 | 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 | 1331 | p += 4; |
| 1325 | 1332 | } |
| 1333 | + | |
| 1334 | + /* Copy and relocate the constant pool data. */ | |
| 1326 | 1335 | data_size = (p_end - p_start) - min_offset; |
| 1327 | 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 | 1378 | if (p == p_start) |
| 1333 | 1379 | goto arm_ret_error; |
| 1334 | 1380 | p -= 4; |
| 1335 | 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 | 1396 | arm_ret_error: |
| 1338 | 1397 | if (!outfile) |
| 1339 | 1398 | printf("%s: invalid epilog\n", name); |
| 1340 | 1399 | } |
| 1341 | - return p - p_start; | |
| 1400 | + return p - p_start; | |
| 1342 | 1401 | } |
| 1343 | 1402 | #endif |
| 1344 | 1403 | |
| ... | ... | @@ -1537,6 +1596,8 @@ void gen_code(const char *name, host_ulong offset, host_ulong size, |
| 1537 | 1596 | } |
| 1538 | 1597 | #elif defined(HOST_ARM) |
| 1539 | 1598 | { |
| 1599 | + uint32_t insn; | |
| 1600 | + | |
| 1540 | 1601 | if ((p_end - p_start) <= 16) |
| 1541 | 1602 | error("%s: function too small", name); |
| 1542 | 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 | 1606 | error("%s: invalid prolog", name); |
| 1546 | 1607 | p_start += 12; |
| 1547 | 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 | 1615 | copy_size = arm_emit_ldr_info(name, start_offset, NULL, p_start, p_end, |
| 1549 | 1616 | relocs, nb_relocs); |
| 1550 | 1617 | } |
| ... | ... | @@ -2282,7 +2349,37 @@ void gen_code(const char *name, host_ulong offset, host_ulong size, |
| 2282 | 2349 | int type; |
| 2283 | 2350 | int addend; |
| 2284 | 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 | 2383 | arm_emit_ldr_info(name, start_offset, outfile, p_start, p_end, |
| 2287 | 2384 | relocs, nb_relocs); |
| 2288 | 2385 | |
| ... | ... | @@ -2303,6 +2400,8 @@ void gen_code(const char *name, host_ulong offset, host_ulong size, |
| 2303 | 2400 | reloc_offset, name, addend); |
| 2304 | 2401 | break; |
| 2305 | 2402 | case R_ARM_PC24: |
| 2403 | + case R_ARM_JUMP24: | |
| 2404 | + case R_ARM_CALL: | |
| 2306 | 2405 | fprintf(outfile, " arm_reloc_pc24((uint32_t *)(gen_code_ptr + %d), 0x%x, %s);\n", |
| 2307 | 2406 | reloc_offset, addend, name); |
| 2308 | 2407 | break; |
| ... | ... | @@ -2407,6 +2506,28 @@ int gen_file(FILE *outfile, int out_type) |
| 2407 | 2506 | |
| 2408 | 2507 | } else { |
| 2409 | 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 | 2531 | fprintf(outfile, |
| 2411 | 2532 | "int dyngen_code(uint8_t *gen_code_buf,\n" |
| 2412 | 2533 | " uint16_t *label_offsets, uint16_t *jmp_offsets,\n" |
| ... | ... | @@ -2417,10 +2538,36 @@ fprintf(outfile, |
| 2417 | 2538 | " const uint32_t *opparam_ptr;\n"); |
| 2418 | 2539 | |
| 2419 | 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 | 2565 | fprintf(outfile, |
| 2421 | 2566 | " uint8_t *last_gen_code_ptr = gen_code_buf;\n" |
| 2422 | 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 | 2571 | #endif |
| 2425 | 2572 | #ifdef HOST_IA64 |
| 2426 | 2573 | { |
| ... | ... | @@ -2489,9 +2636,23 @@ fprintf(outfile, |
| 2489 | 2636 | /* Generate prologue, if needed. */ |
| 2490 | 2637 | |
| 2491 | 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 | 2657 | for(i = 0, sym = symtab; i < nb_syms; i++, sym++) { |
| 2497 | 2658 | const char *name; |
| ... | ... | @@ -2525,17 +2686,6 @@ fprintf(outfile, |
| 2525 | 2686 | " goto the_end;\n" |
| 2526 | 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 | 2690 | fprintf(outfile, |
| 2541 | 2691 | " }\n" |
| ... | ... | @@ -2553,7 +2703,10 @@ fprintf(outfile, |
| 2553 | 2703 | |
| 2554 | 2704 | /* generate some code patching */ |
| 2555 | 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 | 2710 | #endif |
| 2558 | 2711 | /* flush instruction cache */ |
| 2559 | 2712 | fprintf(outfile, "flush_icache_range((unsigned long)gen_code_buf, (unsigned long)gen_code_ptr);\n"); | ... | ... |
dyngen.h
| ... | ... | @@ -19,7 +19,7 @@ |
| 19 | 19 | */ |
| 20 | 20 | |
| 21 | 21 | int __op_param1, __op_param2, __op_param3; |
| 22 | -#ifdef __sparc__ | |
| 22 | +#if defined(__sparc__) || defined(__arm__) | |
| 23 | 23 | void __op_gen_label1(){} |
| 24 | 24 | void __op_gen_label2(){} |
| 25 | 25 | void __op_gen_label3(){} |
| ... | ... | @@ -145,18 +145,16 @@ void fix_bsr(void *p, int offset) { |
| 145 | 145 | |
| 146 | 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 | 150 | typedef struct LDREntry { |
| 154 | 151 | uint8_t *ptr; |
| 155 | 152 | uint32_t *data_ptr; |
| 153 | + unsigned type:2; | |
| 156 | 154 | } LDREntry; |
| 157 | 155 | |
| 158 | 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 | 159 | extern char exec_loop; |
| 162 | 160 | |
| ... | ... | @@ -175,8 +173,9 @@ static uint8_t *arm_flush_ldr(uint8_t *gen_code_ptr, |
| 175 | 173 | int offset, data_size, target; |
| 176 | 174 | uint8_t *data_ptr; |
| 177 | 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 | 180 | if (gen_jmp) { |
| 182 | 181 | /* generate branch to skip the data */ |
| ... | ... | @@ -198,17 +197,48 @@ static uint8_t *arm_flush_ldr(uint8_t *gen_code_ptr, |
| 198 | 197 | offset = ((unsigned long)(le->data_ptr) - (unsigned long)data_start) + |
| 199 | 198 | (unsigned long)data_ptr - |
| 200 | 199 | (unsigned long)ptr - 8; |
| 201 | - insn = *ptr & ~(0xfff | 0x00800000); | |
| 202 | 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 | 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 | 242 | *ptr = insn; |
| 213 | 243 | } |
| 214 | 244 | return gen_code_ptr; | ... | ... |
elf.h
| ... | ... | @@ -502,6 +502,8 @@ typedef struct { |
| 502 | 502 | #define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ |
| 503 | 503 | #define R_ARM_GOT32 26 /* 32 bit GOT entry */ |
| 504 | 504 | #define R_ARM_PLT32 27 /* 32 bit PLT address */ |
| 505 | +#define R_ARM_CALL 28 | |
| 506 | +#define R_ARM_JUMP24 29 | |
| 505 | 507 | #define R_ARM_GNU_VTENTRY 100 |
| 506 | 508 | #define R_ARM_GNU_VTINHERIT 101 |
| 507 | 509 | #define R_ARM_THM_PC11 102 /* thumb unconditional branch */ | ... | ... |