diff options
author | red031000 <rubenru09@aol.com> | 2021-03-21 21:29:54 +0000 |
---|---|---|
committer | red031000 <rubenru09@aol.com> | 2021-03-21 21:29:54 +0000 |
commit | a48d4ca5662481736ae0228f2633928c338c9e8f (patch) | |
tree | a2a12e99bdb459c1d0916a2424d7b93207bb3440 | |
parent | 0ed2967bdbf4667afdbbfafac6aee19d9244d50a (diff) | |
parent | 98aafed459ae6869e00462f265effa77bb76d8a4 (diff) |
Merge branch 'master' of https://github.com/pret/pokediamond
-rw-r--r-- | arm9/arm9.lsf | 4 | ||||
-rw-r--r-- | arm9/asm/nonmatchings/GenerateFontHalfRowLookupTable.s | 1 | ||||
-rw-r--r-- | arm9/asm/unk_02015E30.s | 72 | ||||
-rw-r--r-- | arm9/asm/unk_02020AFC.s | 165 | ||||
-rw-r--r-- | arm9/asm/unk_0208A998.s | 294 | ||||
-rw-r--r-- | arm9/global.inc | 9 | ||||
-rw-r--r-- | arm9/modules/22/asm/module_22.s | 10 | ||||
-rw-r--r-- | arm9/modules/41/asm/module_41.s | 10 | ||||
-rw-r--r-- | arm9/src/error_handling.c | 2 | ||||
-rw-r--r-- | arm9/src/error_message_reset.c | 146 | ||||
-rw-r--r-- | arm9/src/heap.c | 2 | ||||
-rw-r--r-- | arm9/src/main.c | 4 | ||||
-rw-r--r-- | arm9/src/text.c | 88 | ||||
-rw-r--r-- | arm9/src/timer3.c | 66 | ||||
-rw-r--r-- | arm9/src/unk_02015E30.c | 35 | ||||
-rw-r--r-- | include/error_message_reset.h | 39 | ||||
-rw-r--r-- | include/timer3.h | 21 | ||||
-rw-r--r-- | include/unk_02015E30.h | 22 | ||||
-rwxr-xr-x[-rw-r--r--] | tools/asm_processor/asm_processor.py | 2530 | ||||
-rwxr-xr-x | tools/asm_processor/compile.sh | 4 |
20 files changed, 1621 insertions, 1903 deletions
diff --git a/arm9/arm9.lsf b/arm9/arm9.lsf index ba128605..9bd6cb07 100644 --- a/arm9/arm9.lsf +++ b/arm9/arm9.lsf @@ -69,7 +69,7 @@ Static arm9 Object unk_0201E7D8.o Object unk_0201F06C.o Object unk_020208B8.o - Object unk_02020AFC.o + Object timer3.o Object error_handling.o Object unk_02020C44.o Object unk_0202134C.o @@ -317,7 +317,7 @@ Static arm9 Object unk_0208A258.o Object unk_0208A300.o Object unk_0208A338.o - Object unk_0208A998.o + Object error_message_reset.o ### Third-party libraries ### Object unk_0208AC14.o Object custom_allocator.o diff --git a/arm9/asm/nonmatchings/GenerateFontHalfRowLookupTable.s b/arm9/asm/nonmatchings/GenerateFontHalfRowLookupTable.s index a083bf2e..a590ef02 100644 --- a/arm9/asm/nonmatchings/GenerateFontHalfRowLookupTable.s +++ b/arm9/asm/nonmatchings/GenerateFontHalfRowLookupTable.s @@ -1,4 +1,3 @@ - .section .text glabel GenerateFontHalfRowLookupTable diff --git a/arm9/asm/unk_02015E30.s b/arm9/asm/unk_02015E30.s deleted file mode 100644 index 9ca09399..00000000 --- a/arm9/asm/unk_02015E30.s +++ /dev/null @@ -1,72 +0,0 @@ - .include "asm/macros.inc" - .include "global.inc" - - .section .bss - - .global UNK_021C4898 -UNK_021C4898: ; 0x021C4898 - .space 0x20 - - .text - - thumb_func_start FUN_02015E30 -FUN_02015E30: ; 0x02015E30 - ldr r0, _02015E38 ; =UNK_021C4898 - mov r1, #0x0 - str r1, [r0, #0x0] - bx lr - .balign 4 -_02015E38: .word UNK_021C4898 - - thumb_func_start FUN_02015E3C -FUN_02015E3C: ; 0x02015E3C - push {r3, lr} - ldr r1, _02015E5C ; =UNK_021C4898 - mov r2, #0x1 - str r2, [r1, #0x0] - mov r2, #0x0 - str r2, [r1, #0x10] - str r2, [r1, #0x14] - str r2, [r1, #0x8] - str r2, [r1, #0xc] - str r0, [r1, #0x4] - bl FUN_02020BF4 - ldr r2, _02015E5C ; =UNK_021C4898 - str r0, [r2, #0x18] - str r1, [r2, #0x1c] - pop {r3, pc} - .balign 4 -_02015E5C: .word UNK_021C4898 - - thumb_func_start FUN_02015E60 -FUN_02015E60: ; 0x02015E60 - push {r3-r5, lr} - ldr r0, _02015E9C ; =UNK_021C4898 - ldr r0, [r0, #0x0] - cmp r0, #0x0 - beq _02015E9A - bl FUN_02020BF4 - ldr r2, _02015E9C ; =UNK_021C4898 - ldr r3, [r2, #0x18] - ldr r2, [r2, #0x1c] - sub r0, r0, r3 - sbc r1, r2 - bl FUN_02020C14 - add r5, r1, #0x0 - ldr r1, _02015E9C ; =UNK_021C4898 - add r4, r0, #0x0 - ldr r3, [r1, #0x8] - ldr r2, [r1, #0xc] - sub r0, r3, r4 - sbc r2, r5 - bhs _02015E9A - ldr r0, [r1, #0x4] - sub r1, r4, r3 - bl AddIGTSeconds - ldr r0, _02015E9C ; =UNK_021C4898 - str r4, [r0, #0x8] - str r5, [r0, #0xc] -_02015E9A: - pop {r3-r5, pc} - .balign 4 -_02015E9C: .word UNK_021C4898 diff --git a/arm9/asm/unk_02020AFC.s b/arm9/asm/unk_02020AFC.s deleted file mode 100644 index 3d43d259..00000000 --- a/arm9/asm/unk_02020AFC.s +++ /dev/null @@ -1,165 +0,0 @@ - .include "asm/macros.inc" - .include "global.inc" - - .section .bss - - .global UNK_021C59B0 -UNK_021C59B0: ; 0x021C59B0 - .space 0xc - - .text - - thumb_func_start FUN_02020AFC -FUN_02020AFC: ; 0x02020AFC - push {r3, lr} - ldr r0, _02020B24 ; =UNK_021C59B0 - mov r2, #0x0 - str r2, [r0, #0x4] - str r2, [r0, #0x8] - ldr r1, _02020B28 ; =0x0400010E - str r2, [r0, #0x0] - strh r2, [r1, #0x0] - sub r0, r1, #0x2 - strh r2, [r0, #0x0] - mov r0, #0xc1 - strh r0, [r1, #0x0] - ldr r1, _02020B2C ; =FUN_02020B30 - mov r0, #0x40 - bl OS_SetIrqFunction - mov r0, #0x40 - bl OS_EnableIrqMask - pop {r3, pc} - .balign 4 -_02020B24: .word UNK_021C59B0 -_02020B28: .word 0x0400010E -_02020B2C: .word FUN_02020B30 - - thumb_func_start FUN_02020B30 -FUN_02020B30: ; 0x02020B30 - ldr r0, _02020B68 ; =UNK_021C59B0 - mov r3, #0x0 - ldr r1, [r0, #0x4] - ldr r2, [r0, #0x8] - add r1, r1, #0x1 - adc r2, r3 - str r1, [r0, #0x4] - str r2, [r0, #0x8] - ldr r1, [r0, #0x0] - cmp r1, #0x0 - beq _02020B54 - ldr r2, _02020B6C ; =0x0400010E - strh r3, [r2, #0x0] - sub r1, r2, #0x2 - strh r3, [r1, #0x0] - mov r1, #0xc1 - strh r1, [r2, #0x0] - str r3, [r0, #0x0] -_02020B54: - ldr r3, _02020B70 ; =0x027E0000 - ldr r1, _02020B74 ; =0x00003FF8 - mov r0, #0x40 - ldr r2, [r3, r1] - orr r2, r0 - str r2, [r3, r1] - ldr r3, _02020B78 ; =OS_SetIrqFunction - ldr r1, _02020B7C ; =FUN_02020B30 - bx r3 - nop -_02020B68: .word UNK_021C59B0 -_02020B6C: .word 0x0400010E -_02020B70: .word 0x027E0000 -_02020B74: .word 0x00003FF8 -_02020B78: .word OS_SetIrqFunction -_02020B7C: .word FUN_02020B30 - - thumb_func_start FUN_02020B80 -FUN_02020B80: ; 0x02020B80 - push {r4-r5, lr} - sub sp, #0xc - bl OS_DisableInterrupts - ldr r1, _02020BE4 ; =0x0400010C - add r3, sp, #0x0 - ldrh r1, [r1, #0x0] - ldr r2, _02020BE8 ; =0x0000FFFF - strh r1, [r3, #0x0] - ldr r1, _02020BEC ; =UNK_021C59B0 - ldr r5, [r1, #0x4] - ldr r4, [r1, #0x8] - mov r1, #0x0 - mvn r1, r1 - and r1, r5 - str r1, [sp, #0x4] - and r2, r4 - ldr r1, _02020BF0 ; =0x04000214 - str r2, [sp, #0x8] - ldr r2, [r1, #0x0] - mov r1, #0x40 - tst r2, r1 - beq _02020BC4 - ldrh r2, [r3, #0x0] - lsl r1, r1, #0x9 - tst r1, r2 - bne _02020BC4 - ldr r2, [sp, #0x4] - mov r1, #0x0 - ldr r3, [sp, #0x8] - add r2, r2, #0x1 - adc r3, r1 - str r2, [sp, #0x4] - str r3, [sp, #0x8] -_02020BC4: - bl OS_RestoreInterrupts - ldr r2, [sp, #0x4] - ldr r1, [sp, #0x8] - lsr r0, r2, #0x10 - lsl r1, r1, #0x10 - orr r1, r0 - add r0, sp, #0x0 - lsl r3, r2, #0x10 - ldrh r2, [r0, #0x0] - asr r0, r2, #0x1f - orr r1, r0 - add r0, r3, #0x0 - orr r0, r2 - add sp, #0xc - pop {r4-r5, pc} - .balign 4 -_02020BE4: .word 0x0400010C -_02020BE8: .word 0x0000FFFF -_02020BEC: .word UNK_021C59B0 -_02020BF0: .word 0x04000214 - - thumb_func_start FUN_02020BF4 -FUN_02020BF4: ; 0x02020BF4 - ldr r3, _02020BF8 ; =FUN_02020B80 - bx r3 - .balign 4 -_02020BF8: .word FUN_02020B80 - - thumb_func_start FUN_02020BFC -FUN_02020BFC: ; 0x02020BFC - push {r3, lr} - lsr r2, r0, #0x1a - lsl r1, r1, #0x6 - orr r1, r2 - ldr r2, _02020C10 ; =0x000082EA - lsl r0, r0, #0x6 - mov r3, #0x0 - bl _ll_udiv - pop {r3, pc} - .balign 4 -_02020C10: .word 0x000082EA - - thumb_func_start FUN_02020C14 -FUN_02020C14: ; 0x02020C14 - push {r3, lr} - lsr r2, r0, #0x1a - lsl r1, r1, #0x6 - orr r1, r2 - ldr r2, _02020C28 ; =0x01FF6210 - lsl r0, r0, #0x6 - mov r3, #0x0 - bl _ll_udiv - pop {r3, pc} - .balign 4 -_02020C28: .word 0x01FF6210 diff --git a/arm9/asm/unk_0208A998.s b/arm9/asm/unk_0208A998.s deleted file mode 100644 index 14a02753..00000000 --- a/arm9/asm/unk_0208A998.s +++ /dev/null @@ -1,294 +0,0 @@ - .include "asm/macros.inc" - .include "global.inc" - - .extern gMain - - .section .rodata - - .global UNK_020FF49C -UNK_020FF49C: ; 0x020FF49C - .byte 0x00, 0x03, 0x03, 0x1A, 0x12, 0x01, 0x23, 0x00 - - .global UNK_020FF4A4 -UNK_020FF4A4: ; 0x020FF4A4 - .byte 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 - - .global UNK_020FF4AC -UNK_020FF4AC: ; 0x020FF4AC - .byte 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - - .global UNK_020FF4BC -UNK_020FF4BC: ; 0x020FF4BC - .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - .byte 0x01, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - - .global UNK_020FF4D8 -UNK_020FF4D8: ; 0x020FF4D8 - .byte 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - - .section .bss - - .global sErrorMessagePrinterLock -sErrorMessagePrinterLock: ; 0x021C8C58 - .space 0x4 - - .text - - thumb_func_start FUN_0208A998 -FUN_0208A998: ; 0x0208A998 - ldr r3, _0208A9AC ; =0x027E0000 - ldr r1, _0208A9B0 ; =0x00003FF8 - mov r0, #0x1 - ldr r2, [r3, r1] - orr r0, r2 - str r0, [r3, r1] - ldr r3, _0208A9B4 ; =MI_WaitDma - mov r0, #0x3 - bx r3 - nop -_0208A9AC: .word 0x027E0000 -_0208A9B0: .word 0x00003FF8 -_0208A9B4: .word MI_WaitDma - - thumb_func_start PrintErrorMessageAndReset -PrintErrorMessageAndReset: ; 0x0208A9B8 - push {r4-r7, lr} - sub sp, #0x24 - ldr r0, _0208ABC8 ; =sErrorMessagePrinterLock - ldr r1, [r0, #0x0] - cmp r1, #0x1 - bne _0208A9C6 - b _0208ABC4 -_0208A9C6: - mov r1, #0x1 - str r1, [r0, #0x0] - mov r0, #0x0 - bl OS_GetInitArenaHi - add r1, r0, #0x0 - mov r0, #0x0 - bl OS_SetArenaHi - mov r0, #0x0 - bl OS_GetInitArenaLo - add r1, r0, #0x0 - mov r0, #0x0 - bl OS_SetArenaLo - mov r1, #0x1 - ldr r0, _0208ABCC ; =UNK_020FF4A4 - add r2, r1, #0x0 - mov r3, #0x0 - bl FUN_020166C8 - mov r0, #0x0 - add r1, r0, #0x0 - bl FUN_0200E3A0 - mov r0, #0x1 - mov r1, #0x0 - bl FUN_0200E3A0 - mov r0, #0x1 - bl OS_DisableIrqMask - ldr r1, _0208ABD0 ; =FUN_0208A998 - mov r0, #0x1 - bl OS_SetIrqFunction - mov r0, #0x1 - bl OS_EnableIrqMask - mov r0, #0x0 - add r1, r0, #0x0 - bl Main_SetVBlankIntrCB - mov r0, #0x0 - add r1, r0, #0x0 - bl FUN_02015F34 - bl FUN_0201E6D8 - bl FUN_0201E740 - mov r2, #0x1 - lsl r2, r2, #0x1a - ldr r1, [r2, #0x0] - ldr r0, _0208ABD4 ; =0xFFFFE0FF - and r1, r0 - str r1, [r2, #0x0] - ldr r2, _0208ABD8 ; =0x04001000 - ldr r1, [r2, #0x0] - and r0, r1 - str r0, [r2, #0x0] - mov r0, #0x4 - mov r1, #0x8 - bl FUN_0201669C - ldr r0, _0208ABDC ; =gMain + 0x60 - mov r1, #0x0 - strb r1, [r0, #0x5] - bl FUN_0201E7A0 - ldr r3, _0208ABE0 ; =0x04000050 - mov r0, #0x0 - strh r0, [r3, #0x0] - ldr r2, _0208ABE4 ; =0x04001050 - sub r3, #0x50 - strh r0, [r2, #0x0] - ldr r1, [r3, #0x0] - ldr r0, _0208ABE8 ; =0xFFFF1FFF - sub r2, #0x50 - and r1, r0 - str r1, [r3, #0x0] - ldr r1, [r2, #0x0] - and r0, r1 - str r0, [r2, #0x0] - ldr r0, _0208ABEC ; =UNK_020FF4D8 - bl FUN_0201E66C - mov r0, #0x0 - bl FUN_02016B94 - str r0, [sp, #0x10] - ldr r0, _0208ABF0 ; =UNK_020FF4AC - bl FUN_02016BBC - mov r1, #0x0 - ldr r0, [sp, #0x10] - ldr r2, _0208ABF4 ; =UNK_020FF4BC - add r3, r1, #0x0 - bl FUN_02016C18 - ldr r0, [sp, #0x10] - mov r1, #0x0 - bl FUN_02018744 - mov r1, #0x0 - str r1, [sp, #0x0] - ldr r0, [sp, #0x10] - ldr r2, _0208ABF8 ; =0x000001F7 - mov r3, #0x2 - str r1, [sp, #0x4] - bl FUN_0200CB00 - mov r0, #0x0 - mov r1, #0x20 - add r2, r0, #0x0 - bl FUN_02002ED0 - mov r0, #0x0 - mov r1, #0x20 - add r2, r0, #0x0 - add r3, r0, #0x0 - bl FUN_02017F18 - ldr r1, _0208ABFC ; =0x00006C21 - mov r0, #0x0 - bl FUN_02017FE4 - ldr r1, _0208ABFC ; =0x00006C21 - mov r0, #0x4 - bl FUN_02017FE4 - mov r0, #0x1 - mov r1, #0x1a - mov r2, #0xc8 - mov r3, #0x0 - bl NewMsgDataFromNarc - str r0, [sp, #0xc] - mov r0, #0x6 - lsl r0, r0, #0x6 - mov r1, #0x0 - bl String_ctor - add r4, r0, #0x0 - bl FUN_0201BD5C - ldr r0, [sp, #0x10] - ldr r2, _0208AC00 ; =UNK_020FF49C - add r1, sp, #0x14 - bl FUN_02019150 - mov r0, #0xd0 - str r0, [sp, #0x0] - mov r0, #0x90 - mov r2, #0x0 - str r0, [sp, #0x4] - add r0, sp, #0x14 - mov r1, #0xf - add r3, r2, #0x0 - bl FUN_020196F4 - ldr r2, _0208ABF8 ; =0x000001F7 - add r0, sp, #0x14 - mov r1, #0x0 - mov r3, #0x2 - bl FUN_0200CCA4 - ldr r0, [sp, #0xc] - mov r1, #0x3 - add r2, r4, #0x0 - bl ReadMsgDataIntoString - mov r1, #0x0 - str r1, [sp, #0x0] - str r1, [sp, #0x4] - add r0, sp, #0x14 - add r2, r4, #0x0 - add r3, r1, #0x0 - str r1, [sp, #0x8] - bl AddTextPrinterParameterized - add r0, r4, #0x0 - bl String_dtor - bl FUN_0201E788 - mov r0, #0x0 - bl FUN_0200E394 - mov r0, #0x1 - bl FUN_0200E394 - mov r0, #0x0 - mov r1, #0x3f - mov r2, #0x3 - bl FUN_0200A274 - bl FUN_02032DAC - mov r4, #0x1 -_0208AB58: - bl HandleDSLidAction - bl FUN_0202FB80 - bl FUN_02033678 - cmp r0, #0x0 - bne _0208AB72 - add r0, r4, #0x0 - add r1, r4, #0x0 - bl OS_WaitIrq - b _0208AB58 -_0208AB72: - ldr r5, _0208AC04 ; =0x04000130 - ldr r4, _0208AC08 ; =0x027FFFA8 - ldr r7, _0208AC0C ; =0x00002FFF - mov r6, #0x1 -_0208AB7A: - bl HandleDSLidAction - ldrh r1, [r5, #0x0] - ldrh r0, [r4, #0x0] - orr r1, r0 - ldr r0, _0208AC0C ; =0x00002FFF - eor r0, r1 - and r0, r7 - lsl r0, r0, #0x10 - lsr r0, r0, #0x10 - tst r0, r6 - bne _0208AB9C - mov r0, #0x1 - add r1, r0, #0x0 - bl OS_WaitIrq - b _0208AB7A -_0208AB9C: - ldr r1, _0208AC10 ; =0x00007FFF - mov r0, #0x0 - bl FUN_0200E3A0 - ldr r1, _0208AC10 ; =0x00007FFF - mov r0, #0x1 - bl FUN_0200E3A0 - add r0, sp, #0x14 - bl FUN_02019178 - ldr r0, [sp, #0xc] - bl DestroyMsgData - ldr r0, [sp, #0x10] - bl FreeToHeap - mov r0, #0x0 - bl OS_ResetSystem -_0208ABC4: - add sp, #0x24 - pop {r4-r7, pc} - .balign 4 -_0208ABC8: .word sErrorMessagePrinterLock -_0208ABCC: .word UNK_020FF4A4 -_0208ABD0: .word FUN_0208A998 -_0208ABD4: .word 0xFFFFE0FF -_0208ABD8: .word 0x04001000 -_0208ABDC: .word gMain + 0x60 -_0208ABE0: .word 0x04000050 -_0208ABE4: .word 0x04001050 -_0208ABE8: .word 0xFFFF1FFF -_0208ABEC: .word UNK_020FF4D8 -_0208ABF0: .word UNK_020FF4AC -_0208ABF4: .word UNK_020FF4BC -_0208ABF8: .word 0x000001F7 -_0208ABFC: .word 0x00006C21 -_0208AC00: .word UNK_020FF49C -_0208AC04: .word 0x04000130 -_0208AC08: .word 0x027FFFA8 -_0208AC0C: .word 0x00002FFF -_0208AC10: .word 0x00007FFF diff --git a/arm9/global.inc b/arm9/global.inc index ab44d83e..c8834374 100644 --- a/arm9/global.inc +++ b/arm9/global.inc @@ -1353,10 +1353,9 @@ .extern FUN_02020AB0 .extern FUN_02020ACC .extern FUN_02020AE8 -.extern FUN_02020AFC -.extern FUN_02020BF4 -.extern FUN_02020BFC -.extern FUN_02020C14 +.extern GetTimer3Count +.extern Timer3CountToMilliSeconds +.extern Timer3CountToSeconds .extern FUN_02020C44 .extern FUN_02020C90 .extern FUN_02020CB0 @@ -5479,7 +5478,6 @@ .extern FUN_0208A330 .extern FUN_0208A334 .extern FUN_0208A338 -.extern PrintErrorMessageAndReset .extern FUN_0208AC14 .extern FUN_0208AC58 .extern FUN_0208ACEC @@ -8519,3 +8517,4 @@ .extern CARDi_InitCommon .extern MonNotFaintedOrEgg .extern CountAlivePokemon + diff --git a/arm9/modules/22/asm/module_22.s b/arm9/modules/22/asm/module_22.s index ef079cdb..e58e406b 100644 --- a/arm9/modules/22/asm/module_22.s +++ b/arm9/modules/22/asm/module_22.s @@ -657,7 +657,7 @@ MOD22_02254D00: ; 0x02254D00 eor r0, r3 orr r0, r1 beq _02254D32 - bl FUN_02020BF4 + bl GetTimer3Count add r5, r4, #0 add r5, #0xc ldr r7, [r4, #0x14] @@ -672,7 +672,7 @@ MOD22_02254D00: ; 0x02254D00 str r3, [r5, #4] b _02254D3A _02254D32: - bl FUN_02020BF4 + bl GetTimer3Count str r0, [r4, #0xc] str r1, [r4, #0x10] _02254D3A: @@ -687,7 +687,7 @@ _02254D3A: MOD22_02254D44: ; 0x02254D44 push {r4, lr} add r4, r0, #0 - bl FUN_02020BF4 + bl GetTimer3Count str r0, [r4, #0x14] str r1, [r4, #0x18] mov r0, #0 @@ -706,12 +706,12 @@ MOD22_02254D58: ; 0x02254D58 bne _02254D66 b _02254E7C _02254D66: - bl FUN_02020BF4 + bl GetTimer3Count ldr r3, [r5, #0xc] ldr r2, [r5, #0x10] sub r0, r0, r3 sbc r1, r2 - bl FUN_02020BFC + bl Timer3CountToMilliSeconds ldr r2, _02254E80 ; =0x04000280 mov r3, #1 strh r3, [r2] diff --git a/arm9/modules/41/asm/module_41.s b/arm9/modules/41/asm/module_41.s index a60736df..637c100f 100644 --- a/arm9/modules/41/asm/module_41.s +++ b/arm9/modules/41/asm/module_41.s @@ -1046,7 +1046,7 @@ MOD41_0225504C: ; 0x0225504C add r5, r0, #0 add r4, r5, #0 add r4, #0x48 - bl FUN_02020BF4 + bl GetTimer3Count str r0, [r5, #0x68] str r1, [r5, #0x6c] mov r2, #0 @@ -1105,7 +1105,7 @@ MOD41_022550BC: ; 0x022550BC mov r1, #0 add r0, #0x88 str r1, [r0] - bl FUN_02020BF4 + bl GetTimer3Count str r0, [r4, #0x70] str r1, [r4, #0x74] pop {r4, pc} @@ -1115,7 +1115,7 @@ MOD41_022550BC: ; 0x022550BC MOD41_022550D0: ; 0x022550D0 push {r3, r4, r5, r6, r7, lr} add r4, r0, #0 - bl FUN_02020BF4 + bl GetTimer3Count add r5, r4, #0 add r5, #0x68 ldr r7, [r4, #0x70] @@ -1155,12 +1155,12 @@ MOD41_02255108: ; 0x02255108 ldr r0, [r0] cmp r0, #0 beq _0225516A - bl FUN_02020BF4 + bl GetTimer3Count ldr r3, [r4, #0x68] ldr r2, [r4, #0x6c] sub r0, r0, r3 sbc r1, r2 - bl FUN_02020C14 + bl Timer3CountToSeconds add r5, r0, #0 add r0, r4, #0 add r0, #0x80 diff --git a/arm9/src/error_handling.c b/arm9/src/error_handling.c index 9052773f..a01894f6 100644 --- a/arm9/src/error_handling.c +++ b/arm9/src/error_handling.c @@ -1,8 +1,8 @@ #include "global.h" #include "error_handling.h" +#include "error_message_reset.h" extern u32 FUN_02031810(void); -extern void PrintErrorMessageAndReset(void); THUMB_FUNC void ErrorHandling(void) { diff --git a/arm9/src/error_message_reset.c b/arm9/src/error_message_reset.c new file mode 100644 index 00000000..7d1caccb --- /dev/null +++ b/arm9/src/error_message_reset.c @@ -0,0 +1,146 @@ +#include "error_message_reset.h" + +const u32 UNK_020FF49C[2] = { 0x1a030300, 0x00230112 }; + +const u32 UNK_020FF4A4[2] = { 0x00020000, 0x00000000 }; + +const struct GraphicsModes UNK_020FF4AC = { mode1 : 1 }; + +const u32 UNK_020FF4BC[7] = { 0x00, 0x00, 0x0800, 0x00, 0x06000001, 0x0100, 0x00 }; + +const struct GraphicsBanks UNK_020FF4D8 = { bg : 3 }; + +u32 sErrorMessagePrinterLock; + +extern void FUN_0200E3A0(PMLCDTarget, int); +extern void FUN_0201E6D8(); +extern void FUN_0201E7A0(); +extern void FUN_0201E66C(const struct GraphicsBanks *banks); +extern u32 *FUN_02016B94(u32 param0); +extern void FUN_02016BBC(const struct GraphicsModes *modes); +extern void FUN_02016C18(u32 *param0, u32 param1, void *param2, u32 param3); +extern void FUN_02018744(u32 *param0, u32 param1); +extern void FUN_0200CB00(u32 *param0, u32 param1, u32 param2, u32 param3, u8 param4, u32 param5); +extern void FUN_02002ED0(u32 param0, u32 param1, u32 param2); +extern void FUN_02017F18(u32 param0, u32 param1, u32 param2, u32 param3); +extern void FUN_02017FE4(u32 param0, u32 param1); +extern void FUN_02019150(u32 *param0, u32 *param1, const u32 *param2); +extern void FUN_020196F4(u32 *, u8, u16, u16, u16, u16); +extern void FUN_0200CCA4(u32 *param0, u32 param1, u32 param2, u32 param3); +extern void FUN_0201E788(); +extern void FUN_0200E394(u32 param0); +extern void FUN_0200A274(u32 param0, u32 param1, u32 param2); +extern BOOL FUN_02032DAC(void); +extern BOOL FUN_0202FB80(void); +extern BOOL FUN_02033678(void); +extern void FUN_02019178(u32 *param0); +extern void FUN_0201E740(); + +THUMB_FUNC void VBlankHandler() +{ + *(vu32 *)HW_INTR_CHECK_BUF |= 1; + + MI_WaitDma(3); +} + +THUMB_FUNC void PrintErrorMessageAndReset() +{ + + u32 *ptr; + u32 buf[4]; + + if (sErrorMessagePrinterLock != 1) + { + sErrorMessagePrinterLock = 1; + OS_SetArenaHi(OS_ARENA_MAIN, OS_GetInitArenaHi(OS_ARENA_MAIN)); + OS_SetArenaLo(OS_ARENA_MAIN, OS_GetInitArenaLo(OS_ARENA_MAIN)); + + FUN_020166C8((u32 *)UNK_020FF4A4, 1, 1, 0); + FUN_0200E3A0(PM_LCD_TOP, 0); + FUN_0200E3A0(PM_LCD_BOTTOM, 0); + + OS_DisableIrqMask(1); + OS_SetIrqFunction(1, &VBlankHandler); + OS_EnableIrqMask(1); + + Main_SetVBlankIntrCB(NULL, NULL); + + FUN_02015F34(NULL, NULL); + FUN_0201E6D8(); + FUN_0201E740(); + + reg_GX_DISPCNT &= 0xFFFFE0FF; + reg_GXS_DB_DISPCNT &= 0xFFFFE0FF; + + FUN_0201669C(4, 8); + + gMain.unk65 = 0; + FUN_0201E7A0(); + + reg_G2_BLDCNT = 0; + reg_G2S_DB_BLDCNT = 0; + reg_GX_DISPCNT &= 0xFFFF1FFF; + reg_GXS_DB_DISPCNT &= 0xFFFF1FFF; + + FUN_0201E66C(&UNK_020FF4D8); + ptr = FUN_02016B94(0); + FUN_02016BBC(&UNK_020FF4AC); + + FUN_02016C18(ptr, 0, UNK_020FF4BC, 0); + FUN_02018744(ptr, 0); + + FUN_0200CB00(ptr, 0, 503, 2, 0, 0); + + FUN_02002ED0(0, 0x20, 0); + FUN_02017F18(0, 0x20, 0, 0); + FUN_02017FE4(0, 0x6C21); + FUN_02017FE4(4, 0x6C21); + + struct MsgData *msg_data = NewMsgDataFromNarc(1, NARC_MSGDATA_MSG, 0xc8, 0); + struct String *str = String_ctor(6 << 6, 0); + + FUN_0201BD5C(); + FUN_02019150(ptr, buf, UNK_020FF49C); + FUN_020196F4(buf, 15, 0, 0, 0xd0, 0x90); + FUN_0200CCA4(buf, 0, 0x1f7, 2); + + ReadMsgDataIntoString(msg_data, 3, str); + + AddTextPrinterParameterized((u32)buf, 0, (const u16 *)str, 0, 0, 0, NULL); // wtf + + String_dtor(str); + FUN_0201E788(); + FUN_0200E394(0); + FUN_0200E394(1); + FUN_0200A274(0, 0x3f, 3); + FUN_02032DAC(); + + lid: + HandleDSLidAction(); + FUN_0202FB80(); + if (!FUN_02033678()) + { + OS_WaitIrq(1, 1); + goto lid; + } + + + lid2: + HandleDSLidAction(); + if (!((u16)(((reg_PAD_KEYINPUT | *(vu16 *)HW_BUTTON_XY_BUF) ^ 0x2FFF) & 0x2FFF) & 1)) + { + OS_WaitIrq(1, 1); + goto lid2; + } + + FUN_0200E3A0(PM_LCD_TOP, 0x7FFF); + FUN_0200E3A0(PM_LCD_BOTTOM, 0x7FFF); + + FUN_02019178(buf); + + DestroyMsgData(msg_data); + FreeToHeap(ptr); + + OS_ResetSystem(0); + } +} diff --git a/arm9/src/heap.c b/arm9/src/heap.c index c27a40c0..84abc834 100644 --- a/arm9/src/heap.c +++ b/arm9/src/heap.c @@ -1,11 +1,11 @@ #include "heap.h" +#include "error_message_reset.h" extern void *tempName_NNS_FndCreateExpHeapEx(void *param0, u32 param1, u32 param2); extern void *tempName_NNS_FndAllocFromExpHeapEx(void *param0, u32 param1, s32 param2); extern void thunk_FUN_020adc8c(); extern void FUN_020ADDF0(void *ptr1, void *ptr2); extern u32 FUN_02031810(void); -extern void PrintErrorMessageAndReset(void); extern u32 FUN_020ADDC8(void *param0); extern void FUN_020AE82C(u32 param0, void *param1, u32 param2); extern u32 FUN_020ADDC0(void *param0); diff --git a/arm9/src/main.c b/arm9/src/main.c index 86f0f6af..c50fbaff 100644 --- a/arm9/src/main.c +++ b/arm9/src/main.c @@ -11,6 +11,7 @@ #include "poke_overlay.h" #include "player_data.h" #include "sound.h" +#include "timer3.h" FS_EXTERN_OVERLAY(MODULE_52); FS_EXTERN_OVERLAY(MODULE_63); @@ -34,7 +35,6 @@ extern void FUN_02002C14(void); extern void FUN_02002C50(int, int); extern struct SaveBlock2 * SaveBlock2_new(void); extern void * FUN_02029EF8(struct SaveBlock2 *); -extern void FUN_02020AFC(void); extern int FUN_020337E8(int); extern void FUN_02034188(int, int); extern int FUN_020227FC(struct SaveBlock2 *); @@ -75,7 +75,7 @@ THUMB_FUNC void NitroMain(void) gBacklightTop.unk18 = -1; gBacklightTop.unk20 = SaveBlock2_new(); InitSoundData(FUN_02029EF8(gBacklightTop.unk20), Sav2_PlayerData_GetOptionsAddr(gBacklightTop.unk20)); - FUN_02020AFC(); + Init_Timer3(); if (FUN_020337E8(3) == 3) FUN_02034188(3, 0); if (FUN_020227FC(gBacklightTop.unk20) == 0) diff --git a/arm9/src/text.c b/arm9/src/text.c index 6207d913..aadb84c0 100644 --- a/arm9/src/text.c +++ b/arm9/src/text.c @@ -416,93 +416,7 @@ THUMB_FUNC void GenerateFontHalfRowLookupTable(u8 fgColor, u8 bgColor, u8 shadow *(current++) = (shadow12) | temp; } #else -THUMB_FUNC void GenerateFontHalfRowLookupTable(u8 fgColor, u8 bgColor, u8 shadowColor) //TODO use asm preprocessor -{ - asm { - // push {r3-r7, lr} - sub sp, #0x30 - ldr r3, =UNK_021C570C - mov r5, #0x0 - str r5, [sp, #0x20] - str r0, [sp, #0x24] - str r2, [sp, #0x28] - str r1, [sp, #0x2c] - strh r1, [r3, #0x6] - strh r0, [r3, #0x2] - add r0, sp, #0x20 - strh r2, [r3, #0x4] - str r5, [sp, #0x14] - str r0, [sp, #0x8] - mov r12, r0 - mov lr, r0 - str r0, [sp, #0x18] - _0201C07E: - mov r0, #0x0 - str r0, [sp, #0x10] - ldr r0, [sp, #0x18] - str r0, [sp, #0x4] - ldr r0, [sp, #0x8] - ldr r0, [r0, #0x0] - str r0, [sp, #0x1c] - _0201C08C: - mov r0, #0x0 - str r0, [sp, #0xc] - mov r0, lr - str r0, [sp, #0x0] - ldr r0, [sp, #0x4] - ldr r0, [r0, #0x0] - lsl r7, r0, #0x4 - _0201C09A: - ldr r0, [sp, #0x0] - mov r3, #0x0 - ldr r0, [r0, #0x0] - mov r4, r12 - lsl r6, r0, #0x8 - _0201C0A4: - ldr r0, [r4, #0x0] - add r1, r7, #0x0 - lsl r0, r0, #0xc - orr r0, r6 - orr r1, r0 - ldr r0, [sp, #0x1c] - add r3, r3, #0x1 - add r2, r0, #0x0 - orr r2, r1 - lsl r1, r5, #0x1 - ldr r0, =UNK_021C5734 - add r5, r5, #0x1 - add r4, r4, #0x4 - strh r2, [r0, r1] - cmp r3, #0x4 - blt _0201C0A4 - ldr r0, [sp, #0x0] - add r0, r0, #0x4 - str r0, [sp, #0x0] - ldr r0, [sp, #0xc] - add r0, r0, #0x1 - str r0, [sp, #0xc] - cmp r0, #0x4 - blt _0201C09A - ldr r0, [sp, #0x4] - add r0, r0, #0x4 - str r0, [sp, #0x4] - ldr r0, [sp, #0x10] - add r0, r0, #0x1 - str r0, [sp, #0x10] - cmp r0, #0x4 - blt _0201C08C - ldr r0, [sp, #0x8] - add r0, r0, #0x4 - str r0, [sp, #0x8] - ldr r0, [sp, #0x14] - add r0, r0, #0x1 - str r0, [sp, #0x14] - cmp r0, #0x4 - blt _0201C07E - add sp, #0x30 - // pop {r3-r7, pc} - } -} +GLOBAL_ASM("asm/nonmatchings/GenerateFontHalfRowLookupTable.s") #endif THUMB_FUNC void DecompressGlyphTile(const u16 *src, u16 *dst) diff --git a/arm9/src/timer3.c b/arm9/src/timer3.c new file mode 100644 index 00000000..1799fd24 --- /dev/null +++ b/arm9/src/timer3.c @@ -0,0 +1,66 @@ +#include "timer3.h" + +struct Timer3Data timer3_data; + + +THUMB_FUNC void Init_Timer3() +{ + timer3_data.Timer3Counter = 0; + timer3_data.NeedReset = FALSE; + + reg_OS_TM3CNT_H = 0; + reg_OS_TM3CNT_L = 0; + reg_OS_TM3CNT_H = 0xc1; // start timer3 with f/64 and irq enable + + OS_SetIrqFunction(0x40, &CountUpTimer3); + OS_EnableIrqMask(0x40); // irq on timer3 overflow +} + + +THUMB_FUNC void CountUpTimer3() +{ + timer3_data.Timer3Counter++; + + if (timer3_data.NeedReset) + { + reg_OS_TM3CNT_H = 0; + reg_OS_TM3CNT_L = 0; + reg_OS_TM3CNT_H = 0xc1; + timer3_data.NeedReset = FALSE; + } + + *(vu32 *)HW_INTR_CHECK_BUF |= 0x40; + + OS_SetIrqFunction(0x40, &CountUpTimer3); +} + +THUMB_FUNC u64 internal_GetTimer3Count() +{ + OSIntrMode intr_mode = OS_DisableInterrupts(); + + vu16 timer3 = reg_OS_TM3CNT_L; + vu64 timer3_counter = timer3_data.Timer3Counter & 0x0000ffffffffffff; + + if (reg_OS_IF & 0x40 && !(timer3 & 0x8000)) + { + timer3_counter++; + } + + OS_RestoreInterrupts(intr_mode); + return (timer3_counter << 16) | timer3; +} + +THUMB_FUNC u64 GetTimer3Count() +{ + return internal_GetTimer3Count(); +} + +THUMB_FUNC u64 Timer3CountToMilliSeconds(u64 count) +{ + return (count *64) / 33514; +} + +THUMB_FUNC u64 Timer3CountToSeconds(u64 count) +{ + return (count *64) / HW_SYSTEM_CLOCK; +} diff --git a/arm9/src/unk_02015E30.c b/arm9/src/unk_02015E30.c new file mode 100644 index 00000000..8be466fb --- /dev/null +++ b/arm9/src/unk_02015E30.c @@ -0,0 +1,35 @@ + +#include "unk_02015E30.h" + +struct UnkStruct_02015E30 UNK_021C4898; + +THUMB_FUNC void FUN_02015E30() +{ + UNK_021C4898.unk00 = 0; +} + +THUMB_FUNC void FUN_02015E3C(struct IGT *igt) +{ + struct UnkStruct_02015E30 *unk1 = &UNK_021C4898; + UNK_021C4898.unk00 = 1; + UNK_021C4898.unk10 = 0; + UNK_021C4898.unk14 = 0; + UNK_021C4898.unk08 = 0; + UNK_021C4898.unk04 = igt; + + UNK_021C4898.unk18 = GetTimer3Count(); +} + +THUMB_FUNC void FUN_02015E60() +{ + if (UNK_021C4898.unk00 != 0) + { + u64 res = Timer3CountToSeconds(GetTimer3Count() - UNK_021C4898.unk18); + + if (UNK_021C4898.unk08 < res) + { + AddIGTSeconds(UNK_021C4898.unk04, (u32)(res - UNK_021C4898.unk08)); + UNK_021C4898.unk08 = res; + } + } +} diff --git a/include/error_message_reset.h b/include/error_message_reset.h new file mode 100644 index 00000000..d8542df5 --- /dev/null +++ b/include/error_message_reset.h @@ -0,0 +1,39 @@ +#ifndef POKEDIAMOND_ERROR_MESSAGE_RESET_H +#define POKEDIAMOND_ERROR_MESSAGE_RESET_H + +#include "global.h" +#include "heap.h" +#include "SPI_pm.h" +#include "game_init.h" +#include "msgdata.h" +#include "text.h" + + + +struct GraphicsBanks +{ + s32 bg; + s32 bgextpltt; + s32 subbg; + s32 subbgextpltt; + s32 obj; + s32 objextpltt; + s32 subobj; + s32 subobjextpltt; + s32 tex; + s32 pltt; +}; + + +struct GraphicsModes { + u32 mode1; + u32 mode2; + u32 mode3; + u32 mode4; +}; + + +THUMB_FUNC void VBlankHandler(); +THUMB_FUNC void PrintErrorMessageAndReset(); + +#endif //POKEDIAMOND_ERROR_MESSAGE_RESET_H diff --git a/include/timer3.h b/include/timer3.h new file mode 100644 index 00000000..be6e1da8 --- /dev/null +++ b/include/timer3.h @@ -0,0 +1,21 @@ +#ifndef POKEDIAMOND_TIMER3_H +#define POKEDIAMOND_TIMER3_H + + +#include "global.h" + +struct Timer3Data +{ + BOOL NeedReset; + vu64 Timer3Counter; +}; + +THUMB_FUNC void Init_Timer3(); +THUMB_FUNC void CountUpTimer3(); +THUMB_FUNC u64 internal_GetTimer3Count(); +THUMB_FUNC u64 GetTimer3Count(); +THUMB_FUNC u64 Timer3CountToMilliSeconds(u64 count); +THUMB_FUNC u64 Timer3CountToSeconds(u64 count); + + +#endif //POKEDIAMOND_TIMER3_H diff --git a/include/unk_02015E30.h b/include/unk_02015E30.h new file mode 100644 index 00000000..6a4d6c15 --- /dev/null +++ b/include/unk_02015E30.h @@ -0,0 +1,22 @@ +#ifndef POKEDIAMOND_UNK_02015E30_H +#define POKEDIAMOND_UNK_02015E30_H + +#include "global.h" +#include "igt.h" +#include "timer3.h" + +struct UnkStruct_02015E30 +{ + u32 unk00; + struct IGT *unk04; + u64 unk08; + u32 unk10; + u32 unk14; + u64 unk18; +}; + +THUMB_FUNC void FUN_02015E30(); +THUMB_FUNC void FUN_02015E3C(struct IGT *igt); +THUMB_FUNC void FUN_02015E60(); + +#endif //POKEDIAMOND_UNK_02015E30_H diff --git a/tools/asm_processor/asm_processor.py b/tools/asm_processor/asm_processor.py index e1540c0c..ec01c68e 100644..100755 --- a/tools/asm_processor/asm_processor.py +++ b/tools/asm_processor/asm_processor.py @@ -1,1261 +1,1269 @@ -#!/usr/bin/env python3
-import argparse
-import tempfile
-import struct
-import copy
-import sys
-import re
-import os
-from collections import namedtuple, defaultdict
-from io import StringIO
-
-MAX_FN_SIZE = 100
-SLOW_CHECKS = False
-
-EI_NIDENT = 16
-EI_CLASS = 4
-EI_DATA = 5
-EI_VERSION = 6
-EI_OSABI = 7
-EI_ABIVERSION = 8
-STN_UNDEF = 0
-
-SHN_UNDEF = 0
-SHN_ABS = 0xfff1
-SHN_COMMON = 0xfff2
-SHN_XINDEX = 0xffff
-SHN_LORESERVE = 0xff00
-
-STT_NOTYPE = 0
-STT_OBJECT = 1
-STT_FUNC = 2
-STT_SECTION = 3
-STT_FILE = 4
-STT_COMMON = 5
-STT_TLS = 6
-
-STB_LOCAL = 0
-STB_GLOBAL = 1
-STB_WEAK = 2
-
-STV_DEFAULT = 0
-STV_INTERNAL = 1
-STV_HIDDEN = 2
-STV_PROTECTED = 3
-
-SHT_NULL = 0
-SHT_PROGBITS = 1
-SHT_SYMTAB = 2
-SHT_STRTAB = 3
-SHT_RELA = 4
-SHT_HASH = 5
-SHT_DYNAMIC = 6
-SHT_NOTE = 7
-SHT_NOBITS = 8
-SHT_REL = 9
-SHT_SHLIB = 10
-SHT_DYNSYM = 11
-SHT_INIT_ARRAY = 14
-SHT_FINI_ARRAY = 15
-SHT_PREINIT_ARRAY = 16
-SHT_GROUP = 17
-SHT_SYMTAB_SHNDX = 18
-SHT_MIPS_GPTAB = 0x70000003
-SHT_MIPS_DEBUG = 0x70000005
-SHT_MIPS_REGINFO = 0x70000006
-SHT_MIPS_OPTIONS = 0x7000000d
-
-SHF_WRITE = 0x1
-SHF_ALLOC = 0x2
-SHF_EXECINSTR = 0x4
-SHF_MERGE = 0x10
-SHF_STRINGS = 0x20
-SHF_INFO_LINK = 0x40
-SHF_LINK_ORDER = 0x80
-SHF_OS_NONCONFORMING = 0x100
-SHF_GROUP = 0x200
-SHF_TLS = 0x400
-
-R_MIPS_32 = 2
-R_MIPS_26 = 4
-R_MIPS_HI16 = 5
-R_MIPS_LO16 = 6
-
-
-class ElfHeader:
- """
- typedef struct {
- unsigned char e_ident[EI_NIDENT];
- Elf32_Half e_type;
- Elf32_Half e_machine;
- Elf32_Word e_version;
- Elf32_Addr e_entry;
- Elf32_Off e_phoff;
- Elf32_Off e_shoff;
- Elf32_Word e_flags;
- Elf32_Half e_ehsize;
- Elf32_Half e_phentsize;
- Elf32_Half e_phnum;
- Elf32_Half e_shentsize;
- Elf32_Half e_shnum;
- Elf32_Half e_shstrndx;
- } Elf32_Ehdr;
- """
-
- def __init__(self, data):
- self.e_ident = data[:EI_NIDENT]
- self.e_type, self.e_machine, self.e_version, self.e_entry, self.e_phoff, self.e_shoff, self.e_flags, self.e_ehsize, self.e_phentsize, self.e_phnum, self.e_shentsize, self.e_shnum, self.e_shstrndx = struct.unpack('<HHIIIIIHHHHHH', data[EI_NIDENT:])
- assert self.e_ident[EI_CLASS] == 1 # 32-bit
- #assert self.e_ident[EI_DATA] == 2 # big-endian
- #assert self.e_type == 1 # relocatable
- #assert self.e_machine == 8 # MIPS I Architecture
- assert self.e_phoff == 0 # no program header
- assert self.e_shoff != 0 # section header
- assert self.e_shstrndx != SHN_UNDEF
-
- def to_bin(self):
- return self.e_ident + struct.pack('<HHIIIIIHHHHHH', self.e_type,
- self.e_machine, self.e_version, self.e_entry, self.e_phoff,
- self.e_shoff, self.e_flags, self.e_ehsize, self.e_phentsize,
- self.e_phnum, self.e_shentsize, self.e_shnum, self.e_shstrndx)
-
-
-class Symbol:
- """
- typedef struct {
- Elf32_Word st_name;
- Elf32_Addr st_value;
- Elf32_Word st_size;
- unsigned char st_info;
- unsigned char st_other;
- Elf32_Half st_shndx;
- } Elf32_Sym;
- """
-
- def __init__(self, data, strtab):
- self.st_name, self.st_value, self.st_size, st_info, self.st_other, self.st_shndx = struct.unpack('<IIIBBH', data)
- assert self.st_shndx != SHN_XINDEX, "too many sections (SHN_XINDEX not supported)"
- self.bind = st_info >> 4
- self.type = st_info & 15
- self.name = strtab.lookup_str(self.st_name)
- self.visibility = self.st_other & 3
-
- def to_bin(self):
- st_info = (self.bind << 4) | self.type
- return struct.pack('<IIIBBH', self.st_name, self.st_value, self.st_size, st_info, self.st_other, self.st_shndx)
-
-
-class Relocation:
- def __init__(self, data, sh_type):
- self.sh_type = sh_type
- if sh_type == SHT_REL:
- self.r_offset, self.r_info = struct.unpack('<II', data)
- else:
- self.r_offset, self.r_info, self.r_addend = struct.unpack('<III', data)
- self.sym_index = self.r_info >> 8
- self.rel_type = self.r_info & 0xff
-
- def to_bin(self):
- self.r_info = (self.sym_index << 8) | self.rel_type
- if self.sh_type == SHT_REL:
- return struct.pack('<II', self.r_offset, self.r_info)
- else:
- return struct.pack('<III', self.r_offset, self.r_info, self.r_addend)
-
-class Section:
- """
- typedef struct {
- Elf32_Word sh_name;
- Elf32_Word sh_type;
- Elf32_Word sh_flags;
- Elf32_Addr sh_addr;
- Elf32_Off sh_offset;
- Elf32_Word sh_size;
- Elf32_Word sh_link;
- Elf32_Word sh_info;
- Elf32_Word sh_addralign;
- Elf32_Word sh_entsize;
- } Elf32_Shdr;
- """
-
- def __init__(self, header, data, index):
- self.sh_name, self.sh_type, self.sh_flags, self.sh_addr, self.sh_offset, self.sh_size, self.sh_link, self.sh_info, self.sh_addralign, self.sh_entsize = struct.unpack('<IIIIIIIIII', header)
- assert not self.sh_flags & SHF_LINK_ORDER
- if self.sh_entsize != 0:
- assert self.sh_size % self.sh_entsize == 0
- if self.sh_type == SHT_NOBITS:
- self.data = ''
- else:
- self.data = data[self.sh_offset:self.sh_offset + self.sh_size]
- self.index = index
- self.relocated_by = []
-
- @staticmethod
- def from_parts(sh_name, sh_type, sh_flags, sh_link, sh_info, sh_addralign, sh_entsize, data, index):
- header = struct.pack('<IIIIIIIIII', sh_name, sh_type, sh_flags, 0, 0, len(data), sh_link, sh_info, sh_addralign, sh_entsize)
- return Section(header, data, index)
-
- def lookup_str(self, index):
- assert self.sh_type == SHT_STRTAB
- to = self.data.find(b'\0', index)
- assert to != -1
- return self.data[index:to].decode('latin1')
-
- def add_str(self, string):
- assert self.sh_type == SHT_STRTAB
- ret = len(self.data)
- self.data += string.encode('latin1') + b'\0'
- return ret
-
- def is_rel(self):
- return self.sh_type == SHT_REL or self.sh_type == SHT_RELA
-
- def header_to_bin(self):
- if self.sh_type != SHT_NOBITS:
- self.sh_size = len(self.data)
- return struct.pack('<IIIIIIIIII', self.sh_name, self.sh_type, self.sh_flags, self.sh_addr, self.sh_offset, self.sh_size, self.sh_link, self.sh_info, self.sh_addralign, self.sh_entsize)
-
- def late_init(self, sections):
- if self.sh_type == SHT_SYMTAB:
- self.init_symbols(sections)
- elif self.is_rel():
- self.rel_target = sections[self.sh_info]
- self.rel_target.relocated_by.append(self)
- self.init_relocs()
-
- def find_symbol(self, name):
- assert self.sh_type == SHT_SYMTAB
- for s in self.symbol_entries:
- if s.name == name:
- return (s.st_shndx, s.st_value)
- return None
-
- def find_symbol_in_section(self, name, section):
- pos = self.find_symbol(name)
- assert pos is not None
- assert pos[0] == section.index
- return pos[1]
-
- def init_symbols(self, sections):
- assert self.sh_type == SHT_SYMTAB
- assert self.sh_entsize == 16
- self.strtab = sections[self.sh_link]
- entries = []
- for i in range(0, self.sh_size, self.sh_entsize):
- entries.append(Symbol(self.data[i:i+self.sh_entsize], self.strtab))
- self.symbol_entries = entries
-
- def init_relocs(self):
- assert self.is_rel()
- entries = []
- for i in range(0, self.sh_size, self.sh_entsize):
- entries.append(Relocation(self.data[i:i+self.sh_entsize], self.sh_type))
- self.relocations = entries
-
- def local_symbols(self):
- assert self.sh_type == SHT_SYMTAB
- return self.symbol_entries[:self.sh_info]
-
- def global_symbols(self):
- assert self.sh_type == SHT_SYMTAB
- return self.symbol_entries[self.sh_info:]
-
-
-class ElfFile:
- def __init__(self, data):
- self.data = data
- assert data[:4] == b'\x7fELF', "not an ELF file"
-
- self.elf_header = ElfHeader(data[0:52])
-
- offset, size = self.elf_header.e_shoff, self.elf_header.e_shentsize
- null_section = Section(data[offset:offset + size], data, 0)
- num_sections = self.elf_header.e_shnum or null_section.sh_size
-
- self.sections = [null_section]
- for i in range(1, num_sections):
- ind = offset + i * size
- self.sections.append(Section(data[ind:ind + size], data, i))
-
- symtab = None
- for s in self.sections:
- if s.sh_type == SHT_SYMTAB:
- assert not symtab
- symtab = s
- assert symtab is not None
- self.symtab = symtab
-
- shstr = self.sections[self.elf_header.e_shstrndx]
- for s in self.sections:
- s.name = shstr.lookup_str(s.sh_name)
- s.late_init(self.sections)
-
- def find_section(self, name, num):
- i = 0 # Count how many sections of name `name` have been encountered so far, when i reaches `num` return that section
- for s in self.sections:
- if s.name == name and i == num:
- return s
- # Increment if section is a .text section
- if s.name == ".text":
- i += 1
- return None
-
- # Because Metrowerks for DS can make duplicate .text sections
- # for every function, we may need to lookup a specific .text area.
- def find_section_with_name(self, name, st_name):
- for s in self.sections:
- if s.name == name and s.sh_name == st_name:
- return s
- return None
-
- # Return i, where i is the ith text section corresponding to the function
- # called `name`.
- def text_section_index(self, name):
- st_shndx, _ = self.symtab.find_symbol(name)
- n_text = 0
- for sec in self.sections:
- if sec.index == st_shndx:
- return n_text
- if sec.name =='.text':
- n_text += 1
- return -1
-
- def add_section(self, name, sh_type, sh_flags, sh_link, sh_info, sh_addralign, sh_entsize, data):
- shstr = self.sections[self.elf_header.e_shstrndx]
- sh_name = shstr.add_str(name)
- s = Section.from_parts(sh_name=sh_name, sh_type=sh_type,
- sh_flags=sh_flags, sh_link=sh_link, sh_info=sh_info,
- sh_addralign=sh_addralign, sh_entsize=sh_entsize, data=data,
- index=len(self.sections))
- self.sections.append(s)
- s.name = name
- s.late_init(self.sections)
- return s
-
- def drop_irrelevant_sections(self):
- # We can only drop sections at the end, since otherwise section
- # references might be wrong. Luckily, these sections typically are.
- while self.sections[-1].sh_type in [SHT_MIPS_DEBUG, SHT_MIPS_GPTAB]:
- self.sections.pop()
-
- def write(self, filename):
- outfile = open(filename, 'wb')
- outidx = 0
- def write_out(data):
- nonlocal outidx
- outfile.write(data)
- outidx += len(data)
- def pad_out(align):
- if align and outidx % align:
- write_out(b'\0' * (align - outidx % align))
-
- self.elf_header.e_shnum = len(self.sections)
- write_out(self.elf_header.to_bin())
-
- for s in self.sections:
- if s.sh_type != SHT_NOBITS and s.sh_type != SHT_NULL:
- pad_out(s.sh_addralign)
- s.sh_offset = outidx
- write_out(s.data)
-
- pad_out(4)
- self.elf_header.e_shoff = outidx
- for s in self.sections:
- write_out(s.header_to_bin())
-
- outfile.seek(0)
- outfile.write(self.elf_header.to_bin())
- outfile.close()
-
-
-def is_temp_name(name):
- return name.startswith('_asmpp_')
-
-
-# https://stackoverflow.com/a/241506
-def re_comment_replacer(match):
- s = match.group(0)
- if s[0] in "/#":
- return " "
- else:
- return s
-
-
-re_comment_or_string = re.compile(
- r'#.*|/\*.*?\*/|"(?:\\.|[^\\"])*"'
-)
-
-
-class Failure(Exception):
- def __init__(self, message):
- self.message = message
-
- def __str__(self):
- return self.message
-
-
-class GlobalState:
- def __init__(self, min_instr_count, skip_instr_count, use_jtbl_for_rodata):
- # A value that hopefully never appears as a 32-bit rodata constant (or we
- # miscompile late rodata). Increases by 1 in each step.
- self.late_rodata_hex = 0xE0123456
- self.namectr = 0
- self.min_instr_count = min_instr_count
- self.skip_instr_count = skip_instr_count
- self.use_jtbl_for_rodata = use_jtbl_for_rodata
-
- def next_late_rodata_hex(self):
- dummy_bytes = struct.pack('<I', self.late_rodata_hex)
- if (self.late_rodata_hex & 0xffff) == 0:
- # Avoid lui
- self.late_rodata_hex += 1
- self.late_rodata_hex += 1
- return dummy_bytes
-
- def make_name(self, cat):
- self.namectr += 1
- return '_asmpp_{}{}'.format(cat, self.namectr)
-
-
-Function = namedtuple('Function', ['text_glabels', 'asm_conts', 'late_rodata_dummy_bytes', 'jtbl_rodata_size', 'late_rodata_asm_conts', 'fn_desc', 'data'])
-
-
-class GlobalAsmBlock:
- def __init__(self, fn_desc):
- self.fn_desc = fn_desc
- self.cur_section = '.text'
- self.asm_conts = []
- self.late_rodata_asm_conts = []
- self.late_rodata_alignment = 0
- self.late_rodata_alignment_from_content = False
- self.text_glabels = []
- self.fn_section_sizes = {
- '.text': 0,
- '.init': 0,
- '.data': 0,
- '.bss': 0,
- '.rodata': 0,
- '.sdata': 0,
- '.sdata2': 0,
- '.sbss': 0,
- #'.sbss2': 0,
- '.late_rodata': 0,
- }
- self.fn_ins_inds = []
- self.glued_line = ''
- self.num_lines = 0
-
- def fail(self, message, line=None):
- context = self.fn_desc
- if line:
- context += ", at line \"" + line + "\""
- raise Failure(message + "\nwithin " + context)
-
- def count_quoted_size(self, line, z, real_line, output_enc):
- line = line.encode(output_enc).decode('latin1')
- in_quote = False
- num_parts = 0
- ret = 0
- i = 0
- digits = "0123456789" # 0-7 would be more sane, but this matches GNU as
- while i < len(line):
- c = line[i]
- i += 1
- if not in_quote:
- if c == '"':
- in_quote = True
- num_parts += 1
- else:
- if c == '"':
- in_quote = False
- continue
- ret += 1
- if c != '\\':
- continue
- if i == len(line):
- self.fail("backslash at end of line not supported", real_line)
- c = line[i]
- i += 1
- # (if c is in "bfnrtv", we have a real escaped literal)
- if c == 'x':
- # hex literal, consume any number of hex chars, possibly none
- while i < len(line) and line[i] in digits + "abcdefABCDEF":
- i += 1
- elif c in digits:
- # octal literal, consume up to two more digits
- it = 0
- while i < len(line) and line[i] in digits and it < 2:
- i += 1
- it += 1
-
- if in_quote:
- self.fail("unterminated string literal", real_line)
- if num_parts == 0:
- self.fail(".ascii with no string", real_line)
- return ret + num_parts if z else ret
-
-
- def align4(self):
- while self.fn_section_sizes[self.cur_section] % 2 != 0:
- self.fn_section_sizes[self.cur_section] += 1
-
- def add_sized(self, size, line):
- if self.cur_section in ['.text', '.init', '.late_rodata']:
- if size % 2 != 0:
- self.fail("size must be a multiple of 2 or 4", line)
- if size < 0:
- self.fail("size cannot be negative", line)
- self.fn_section_sizes[self.cur_section] += size
- if self.cur_section in ['.text', '.init']:
- if not self.text_glabels:
- self.fail(".text or .init block without an initial glabel", line)
- self.fn_ins_inds.append((self.num_lines - 1, size // 2))
-
- def process_line(self, line, output_enc):
- self.num_lines += 1
- if line.endswith('\\'):
- self.glued_line += line[:-1]
- return
- line = self.glued_line + line
- self.glued_line = ''
-
- real_line = line
- line = re.sub(re_comment_or_string, re_comment_replacer, line)
- line = line.strip()
- line = re.sub(r'^[a-zA-Z0-9_]+:\s*', '', line)
- changed_section = False
- emitting_double = False
- if line.startswith('glabel ') and self.cur_section in ['.text', '.init']:
- self.text_glabels.append(line.split()[1])
- if not line:
- pass # empty line
- elif line.startswith('glabel ') or (' ' not in line and line.endswith(':')):
- pass # label
- elif line.startswith('.section') or line in ['.text', '.init', '.data', '.rdata', '.rodata', '.sdata', '.sdata2', '.bss','.sbss', '.late_rodata']:
- # section change
- self.cur_section = '.rodata' if line == '.rdata' else line.split(',')[0].split()[-1]
- if self.cur_section not in ['.data', '.text', '.init', '.rodata', '.sdata', '.sdata2', '.late_rodata', '.bss', '.sbss']:
- self.fail("unrecognized .section directive", real_line)
- changed_section = True
- elif line.startswith('.late_rodata_alignment'):
- if self.cur_section != '.late_rodata':
- self.fail(".late_rodata_alignment must occur within .late_rodata section", real_line)
- value = int(line.split()[1])
- if value not in [4, 8]:
- self.fail(".late_rodata_alignment argument must be 4 or 8", real_line)
- if self.late_rodata_alignment and self.late_rodata_alignment != value:
- self.fail(".late_rodata_alignment alignment assumption conflicts with earlier .double directive. Make sure to provide explicit alignment padding.")
- self.late_rodata_alignment = value
- changed_section = True
- elif line.startswith('.incbin'):
- self.add_sized(int(line.split(',')[-1].strip(), 0), real_line)
- elif line.startswith('.skip'):
- self.add_sized(int(line.split()[-1].strip(), 0), real_line)
- elif line.startswith('.long') or line.startswith('.float'):
- self.align4()
- self.add_sized(4 * len(line.split(',')), real_line)
- elif line.startswith('.double'):
- self.align4()
- if self.cur_section == '.late_rodata':
- align8 = self.fn_section_sizes[self.cur_section] % 8
- # Automatically set late_rodata_alignment, so the generated C code uses doubles.
- # This gives us correct alignment for the transferred doubles even when the
- # late_rodata_alignment is wrong, e.g. for non-matching compilation.
- if not self.late_rodata_alignment:
- self.late_rodata_alignment = 8 - align8
- self.late_rodata_alignment_from_content = True
- elif self.late_rodata_alignment != 8 - align8:
- if self.late_rodata_alignment_from_content:
- self.fail("found two .double directives with different start addresses mod 8. Make sure to provide explicit alignment padding.", real_line)
- else:
- self.fail(".double at address that is not 0 mod 8 (based on .late_rodata_alignment assumption). Make sure to provide explicit alignment padding.", real_line)
- self.add_sized(8 * len(line.split(',')), real_line)
- emitting_double = True
- elif line.startswith('.space'):
- self.add_sized(int(line.split()[1], 0), real_line)
- elif line.startswith('.balign') or line.startswith('.align'):
- align = int(line.split()[1])
- if align != 4:
- self.fail("only .balign 4 is supported", real_line)
- self.align4()
- elif line.startswith('.asci'):
- z = (line.startswith('.asciz') or line.startswith('.asciiz'))
- self.add_sized(self.count_quoted_size(line, z, real_line, output_enc), real_line)
- elif line.startswith('.byte'):
- self.add_sized(len(line.split(',')), real_line)
- # Branches are 4 bytes long
- elif line.startswith('bl'):
- self.add_sized(4, real_line)
- else:
- # Unfortunately, macros are hard to support for .rodata --
- # we don't know how how space they will expand to before
- # running the assembler, but we need that information to
- # construct the C code. So if we need that we'll either
- # need to run the assembler twice (at least in some rare
- # cases), or change how this program is invoked.
- # Similarly, we can't currently deal with pseudo-instructions
- # that expand to several real instructions.
- if self.cur_section != '.text' and self.cur_section != '.init':
- self.fail("instruction or macro call in non-.text/.init section? not supported", real_line)
- self.add_sized(2, real_line)
- if self.cur_section == '.late_rodata':
- if not changed_section:
- if emitting_double:
- self.late_rodata_asm_conts.append(".align 0")
- self.late_rodata_asm_conts.append(real_line)
- if emitting_double:
- self.late_rodata_asm_conts.append(".align 2")
- else:
- self.asm_conts.append(real_line)
-
- def finish(self, state):
- src = [''] * (self.num_lines + 1)
- late_rodata_dummy_bytes = []
- jtbl_rodata_size = 0
- late_rodata_fn_output = []
-
- num_instr = self.fn_section_sizes['.text'] // 2
-
- if self.fn_section_sizes['.late_rodata'] > 0:
- # Generate late rodata by emitting unique float constants.
- # This requires 3 instructions for each 4 bytes of rodata.
- # If we know alignment, we can use doubles, which give 3
- # instructions for 8 bytes of rodata.
- size = self.fn_section_sizes['.late_rodata'] // 2
- skip_next = False
- needs_double = (self.late_rodata_alignment != 0)
- for i in range(size):
- if skip_next:
- skip_next = False
- continue
- # Jump tables give 9 instructions for >= 5 words of rodata, and should be
- # emitted when:
- # - -O2 or -O2 -g3 are used, which give the right codegen
- # - we have emitted our first .float/.double (to ensure that we find the
- # created rodata in the binary)
- # - we have emitted our first .double, if any (to ensure alignment of doubles
- # in shifted rodata sections)
- # - we have at least 5 words of rodata left to emit (otherwise IDO does not
- # generate a jump table)
- # - we have at least 10 more instructions to go in this function (otherwise our
- # function size computation will be wrong since the delay slot goes unused)
- if (not needs_double and state.use_jtbl_for_rodata and i >= 1 and
- size - i >= 5 and num_instr - len(late_rodata_fn_output) >= 10):
- cases = " ".join("case {}:".format(case) for case in range(size - i))
- late_rodata_fn_output.append("switch (*(volatile int*)0) { " + cases + " ; }")
- late_rodata_fn_output.extend([""] * 8)
- jtbl_rodata_size = (size - i) * 4
- break
- dummy_bytes = state.next_late_rodata_hex()
- late_rodata_dummy_bytes.append(dummy_bytes)
- if self.late_rodata_alignment == 4 * ((i + 1) % 2 + 1) and i + 1 < size:
- dummy_bytes2 = state.next_late_rodata_hex()
- late_rodata_dummy_bytes.append(dummy_bytes2)
- fval, = struct.unpack('<d', dummy_bytes + dummy_bytes2)
- late_rodata_fn_output.append('*(volatile double*)0 = {};'.format(fval))
- skip_next = True
- needs_double = True
- else:
- fval, = struct.unpack('<f', dummy_bytes)
- late_rodata_fn_output.append('*(volatile float*)0 = {}f;'.format(fval))
- late_rodata_fn_output.append('')
- late_rodata_fn_output.append('')
-
- text_name = None
- if self.fn_section_sizes['.text'] > 0 or late_rodata_fn_output:
- text_name = state.make_name('func')
- src[0] = 'int {}(void) {{ return '.format(text_name)
- instr_count = self.fn_section_sizes['.text'] // 2
- src[self.num_lines] = '((volatile void *) 0); }; ' if instr_count > 1 else '; }; '
- if instr_count < state.min_instr_count:
- self.fail("too short .text block")
- tot_emitted = 0
- tot_skipped = 0
- fn_emitted = 0
- fn_skipped = 0
- rodata_stack = late_rodata_fn_output[::-1]
- for (line, count) in self.fn_ins_inds:
- for _ in range(count):
- if (fn_emitted > MAX_FN_SIZE and instr_count - tot_emitted > state.min_instr_count and
- (not rodata_stack or rodata_stack[-1])):
- # Don't let functions become too large. When a function reaches 284
- # instructions, and -O2 -framepointer flags are passed, the IRIX
- # compiler decides it is a great idea to start optimizing more.
- fn_emitted = 0
- fn_skipped = 0
- src[line] += '((volatile void *) 0); }} int {}(void) {{ return '.format(state.make_name('large_func'))
- if fn_skipped < state.skip_instr_count:
- fn_skipped += 1
- tot_skipped += 1
- elif rodata_stack:
- src[line] += rodata_stack.pop()
- else:
- src[line] += '*(int *)'
- tot_emitted += 1
- fn_emitted += 1
- if rodata_stack:
- size = len(late_rodata_fn_output) // 3
- available = instr_count - tot_skipped
- self.fail(
- "late rodata to text ratio is too high: {} / {} must be <= 1/3\n"
- "add .late_rodata_alignment (4|8) to the .late_rodata "
- "block to double the allowed ratio."
- .format(size, available))
-
- init_name = None
- if self.fn_section_sizes['.init'] > 0 or late_rodata_fn_output:
- init_name = state.make_name('func')
- src[0] = 'int {}(void) {{ return '.format(init_name)
- instr_count = self.fn_section_sizes['.init'] // 2
- src[self.num_lines] = '((volatile void *) 0); }; ' if instr_count else '; }; '
- if instr_count < state.min_instr_count:
- self.fail("too short .init block")
- tot_emitted = 0
- tot_skipped = 0
- fn_emitted = 0
- fn_skipped = 0
- rodata_stack = late_rodata_fn_output[::-1]
- for (line, count) in self.fn_ins_inds:
- for _ in range(count):
- if (fn_emitted > MAX_FN_SIZE and instr_count - tot_emitted > state.min_instr_count and
- (not rodata_stack or rodata_stack[-1])):
- # Don't let functions become too large. When a function reaches 284
- # instructions, and -O2 -framepointer flags are passed, the IRIX
- # compiler decides it is a great idea to start optimizing more.
- fn_emitted = 0
- fn_skipped = 0
- src[line] += '((volatile void *) 0); }} int {}(void) {{ return '.format(state.make_name('large_func'))
- if fn_skipped < state.skip_instr_count:
- fn_skipped += 1
- tot_skipped += 1
- elif rodata_stack:
- src[line] += rodata_stack.pop()
- else:
- src[line] += '*(int *)'
- tot_emitted += 1
- fn_emitted += 1
- if rodata_stack:
- size = len(late_rodata_fn_output) // 3
- available = instr_count - tot_skipped
- self.fail(
- "late rodata to init ratio is too high: {} / {} must be <= 1/3\n"
- "add .late_rodata_alignment (4|8) to the .late_rodata "
- "block to double the allowed ratio."
- .format(size, available))
-
- rodata_name = None
- if self.fn_section_sizes['.rodata'] > 0:
- rodata_name = state.make_name('rodata')
- src[self.num_lines] += f" const char {rodata_name}[{self.fn_section_sizes['.rodata']}] = {{1}};"
-
- data_name = None
- if self.fn_section_sizes['.data'] > 0:
- data_name = state.make_name('data')
- src[self.num_lines] += f" char {data_name}[{self.fn_section_sizes['.data']}] = {{1}};"
-
- bss_name = None
- if self.fn_section_sizes['.bss'] > 0:
- bss_name = state.make_name('bss')
- src[self.num_lines] += f" char {bss_name}[{self.fn_section_sizes['.bss']}];"
-
- sdata_name = None # sdata is like data but small
- if self.fn_section_sizes['.sdata'] > 0:
- sdata_code = ""
- for i in range(self.fn_section_sizes['.sdata']):
- sdata_name = state.make_name('sdata')
- sdata_code += f" char {sdata_name} = 1;"
- src[self.num_lines] += sdata_code
-
- sdata2_name = None # sdata2 is like rodata but small
- if self.fn_section_sizes['.sdata2'] > 0:
- sdata2_code = ""
- for i in range(self.fn_section_sizes['.sdata2']):
- sdata2_name = state.make_name('sdata2')
- sdata2_code += f" const char {sdata2_name} = 1;"
- src[self.num_lines] += sdata2_code
-
- sbss_name = None # Similarly, sbss is like uninitialized data but small
- if self.fn_section_sizes['.sbss'] > 0:
- sbss_code = ""
- for i in range(self.fn_section_sizes['.sbss']):
- sbss_name = state.make_name('sbss')
- sbss_code += f" char {sbss_name};"
- src[self.num_lines] += sbss_code
-
- """ sbss2 is currently borked
- sbss2_name = None # Similarly, sbss2 is like uninitialized rodata but small
- if self.fn_section_sizes['.sbss2'] > 0:
- sbss2_code = ""
- for i in range(self.fn_section_sizes['.sbss2']):
- sbss2_name = state.make_name('sbss2')
- sbss2_code += f" const char {sbss2_name};"
- src[self.num_lines] += sbss2_code
- """
-
- fn = Function(
- text_glabels=self.text_glabels,
- asm_conts=self.asm_conts,
- late_rodata_dummy_bytes=late_rodata_dummy_bytes,
- jtbl_rodata_size=jtbl_rodata_size,
- late_rodata_asm_conts=self.late_rodata_asm_conts,
- fn_desc=self.fn_desc,
- data={
- '.text': (text_name, self.fn_section_sizes['.text']),
- '.data': (data_name, self.fn_section_sizes['.data']),
- '.rodata': (rodata_name, self.fn_section_sizes['.rodata']),
- '.bss': (bss_name, self.fn_section_sizes['.bss']),
- '.sdata': (sdata_name, self.fn_section_sizes['.sdata']),
- '.sdata2': (sdata2_name, self.fn_section_sizes['.sdata2']),
- '.sbss': (sbss_name, self.fn_section_sizes['.sbss']),
- #'.sbss2': (sbss2_name, self.fn_section_sizes['.sbss2']),
- })
- return src, fn
-
-cutscene_data_regexpr = re.compile(r"CutsceneData (.|\n)*\[\] = {")
-float_regexpr = re.compile(r"[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?f")
-
-def repl_float_hex(m):
- return str(struct.unpack("<I", struct.pack("<f", float(m.group(0).strip().rstrip("f"))))[0])
-
-def parse_source(f, opt, framepointer, input_enc, output_enc, print_source=None):
- opt = "O4"
- min_instr_count = 3 # idk
- skip_instr_count = 2 # idk
-
- use_jtbl_for_rodata = False
- if opt in ['O2', 'g3'] and not framepointer:
- use_jtbl_for_rodata = True
-
- state = GlobalState(min_instr_count, skip_instr_count, use_jtbl_for_rodata)
-
- global_asm = None
- asm_functions = []
- output_lines = []
-
- is_cutscene_data = False
-
- for line_no, raw_line in enumerate(f, 1):
- raw_line = raw_line.rstrip()
- line = raw_line.lstrip()
-
- # Print exactly one output line per source line, to make compiler
- # errors have correct line numbers. These will be overridden with
- # reasonable content further down.
- output_lines.append('')
- if global_asm is not None:
- if line.startswith(')'):
- src, fn = global_asm.finish(state)
- for i, line2 in enumerate(src):
- output_lines[start_index + i] = line2
- asm_functions.append(fn)
- global_asm = None
- else:
- global_asm.process_line(raw_line, output_enc)
- else:
- if line in ['GLOBAL_ASM(', '#pragma GLOBAL_ASM(']:
- global_asm = GlobalAsmBlock("GLOBAL_ASM block at line " + str(line_no))
- start_index = len(output_lines)
- elif ((line.startswith('GLOBAL_ASM("') or line.startswith('#pragma GLOBAL_ASM("'))
- and line.endswith('")')):
- fname = line[line.index('(') + 2 : -2]
- global_asm = GlobalAsmBlock(fname)
- with open(fname, encoding=input_enc) as f:
- for line2 in f:
- global_asm.process_line(line2.rstrip(), output_enc)
- src, fn = global_asm.finish(state)
- output_lines[-1] = ''.join(src)
- asm_functions.append(fn)
- global_asm = None
- elif ((line.startswith('#include "')) and line.endswith('" EARLY')):
- # C includes qualified with EARLY (i.e. #include "file.c" EARLY) will be
- # processed recursively when encountered
- fpath = os.path.dirname(f.name)
- fname = line[line.index(' ') + 2 : -7]
- include_src = StringIO()
- with open(fpath + os.path.sep + fname, encoding=input_enc) as include_file:
- parse_source(include_file, opt, framepointer, input_enc, output_enc, include_src)
- output_lines[-1] = include_src.getvalue()
- include_src.write('#line ' + str(line_no) + '\n')
- include_src.close()
- else:
- # This is a hack to replace all floating-point numbers in an array of a particular type
- # (in this case CutsceneData) with their corresponding IEEE-754 hexadecimal representation
- if cutscene_data_regexpr.search(line) is not None:
- is_cutscene_data = True
- elif line.endswith("};"):
- is_cutscene_data = False
- if is_cutscene_data:
- raw_line = re.sub(float_regexpr, repl_float_hex, raw_line)
- output_lines[-1] = raw_line
-
- if print_source:
- if isinstance(print_source, StringIO):
- for line in output_lines:
- print_source.write(line + '\n')
- else:
- for line in output_lines:
- print_source.write(line.encode(output_enc) + b'\n')
- print_source.flush()
- if print_source != sys.stdout.buffer:
- print_source.close()
- out_file = open("output.txt", 'w')
- out_file.write(str(asm_functions))
- out_file.close()
- return asm_functions
-
-def fixup_objfile(objfile_name, functions, asm_prelude, assembler, output_enc):
- SECTIONS = ['.data']
- SECTIONS.extend(['.text' for i in range(0,len(functions))])
- SECTIONS.extend(['.rodata', '.bss', '.sdata', '.sdata2', '.sbss'])
-
- with open(objfile_name, 'rb') as f:
- objfile = ElfFile(f.read())
-
- prev_locs = defaultdict(int)
- to_copy = defaultdict(list)
-
- asm = []
- all_late_rodata_dummy_bytes = []
- all_jtbl_rodata_size = []
- late_rodata_asm = []
- late_rodata_source_name_start = None
- late_rodata_source_name_end = None
-
- # Generate an assembly file with all the assembly we need to fill in. For
- # simplicity we pad with nops/.space so that addresses match exactly, so we
- # don't have to fix up relocations/symbol references.
- all_text_glabels = set()
- for function in functions:
- ifdefed = False
- for sectype, (temp_name, size) in function.data.items():
- if temp_name is None:
- continue
- assert size > 0
- n_text = objfile.text_section_index(temp_name)
- loc = objfile.symtab.find_symbol(temp_name)
- if loc is None:
- ifdefed = True
- break
- loc = loc[1]
- prev_loc = prev_locs[sectype + (str(n_text) if sectype == '.text' else '')]
- if loc < prev_loc:
- raise Failure("Wrongly computed size for section {} (diff {}). This is an asm-processor bug!".format(sectype + (str(n_text) if sectype == '.text' else ''), prev_loc- loc))
- if loc != prev_loc:
- asm.append('.section ' + sectype)
- if sectype == '.text':
- for i in range((loc - prev_loc) // 2):
- asm.append('nop')
- else:
- asm.append('.space {}'.format(loc - prev_loc))
- to_copy[sectype + (str(n_text) if sectype == '.text' else '')].append((loc, size, temp_name, function.fn_desc))
- prev_locs[sectype + (str(n_text) if sectype == '.text' else '')] = loc + size
- if not ifdefed:
- all_text_glabels.update(function.text_glabels)
- all_late_rodata_dummy_bytes.append(function.late_rodata_dummy_bytes)
- all_jtbl_rodata_size.append(function.jtbl_rodata_size)
- late_rodata_asm.append(function.late_rodata_asm_conts)
- for sectype, (temp_name, size) in function.data.items():
- if temp_name is not None:
- asm.append('.section ' + sectype)
- asm.append('glabel ' + temp_name + '_asm_start')
- asm.append('.section .text')
- for line in function.asm_conts:
- asm.append(line)
- for sectype, (temp_name, size) in function.data.items():
- if temp_name is not None:
- #asm.append('.section ' + sectype)
- asm.append('glabel ' + temp_name + '_asm_end')
-
- if any(late_rodata_asm):
- late_rodata_source_name_start = '_asmpp_late_rodata_start'
- late_rodata_source_name_end = '_asmpp_late_rodata_end'
- asm.append('.rdata')
- asm.append('glabel {}'.format(late_rodata_source_name_start))
- for conts in late_rodata_asm:
- asm.extend(conts)
- asm.append('glabel {}'.format(late_rodata_source_name_end))
-
- o_file = open("asm_processor_temp.o", 'w').close() # Create temp file. tempfile module isn't working for me.
- o_name = "asm_processor_temp.o"
-
- s_file = open("asm_processor_temp.s", 'wb') # Ditto.
- s_name = "asm_processor_temp.s"
- try:
- s_file.write(asm_prelude + b'\n')
- for line in asm:
- s_file.write(line.encode(output_enc) + b'\n')
- s_file.close()
- ret = os.system(assembler + " " + s_name + " -o " + o_name)
- if ret != 0:
- raise Failure("failed to assemble")
- with open(o_name, 'rb') as f:
- asm_objfile = ElfFile(f.read())
-
- # Remove some clutter from objdump output
- objfile.drop_irrelevant_sections()
-
- """
- # Unify reginfo sections
- target_reginfo = objfile.find_section('.reginfo')
- source_reginfo_data = list(asm_objfile.find_section('.reginfo').data)
- data = list(target_reginfo.data)
- for i in range(20):
- data[i] |= source_reginfo_data[i]
- target_reginfo.data = bytes(data)
- """
-
- # Move over section contents
- modified_text_positions = set()
- jtbl_rodata_positions = set()
- last_rodata_pos = 0
- n_text = 0
- for sec in objfile.sections:
- sectype = sec.name
- if not to_copy[sectype + (str(n_text) if sectype == '.text' else '')]:
- if sectype == '.text':
- n_text += 1
- continue
- # This should work as long as you NONMATCH whole functions rather than asm fragments
- func = to_copy[sectype + str(n_text) if sectype == '.text' else ''][0][2]
- asm_n_text = asm_objfile.text_section_index(func + '_asm_start')
- source = asm_objfile.find_section(sectype, asm_n_text if sectype == '.text' else 0)
- assert source is not None, "didn't find source section: " + sectype
- for (pos, count, temp_name, fn_desc) in to_copy[sectype + (str(n_text) if sectype == '.text' else '')]:
- loc1 = asm_objfile.symtab.find_symbol_in_section(temp_name + '_asm_start', source)
- loc2 = asm_objfile.symtab.find_symbol_in_section(temp_name + '_asm_end', source)
- assert loc1 == pos, "assembly and C files don't line up for section " + sectype + ", " + fn_desc
- # Since we are nonmatching whole functions, we don't need to insert the correct
- # amount of padding into the src file. We don't actually need to insert padding
- # at all. We can just plop the asm's text section into the objfile.
- # if loc2 - loc1 != count:
- # raise Failure("incorrectly computed size for section " + sectype + ", " + fn_desc + ". If using .double, make sure to provide explicit alignment padding.")
- if sectype == '.bss' or sectype == '.sbss2':
- continue
- target = objfile.find_section(sectype, n_text if sectype == '.text' else 0)
- assert target is not None, "missing target section of type " + sectype
- data = list(target.data)
- for (pos, count, _, _) in to_copy[sectype + (str(n_text) if sectype == '.text' else '')]:
- # mwasmarm 4-aligns text sections, so make sure to copy exactly `count` bytes
- data[pos:pos + count] = source.data[pos:pos + count]
- if sectype == '.text':
- assert count % 2 == 0
- assert pos % 2 == 0
- for i in range(count // 2):
- modified_text_positions.add(pos + 2 * i)
- elif sectype == '.rodata':
- last_rodata_pos = pos + count
- target.data = bytes(data)
- if sectype == '.text':
- n_text += 1
-
- # Move over late rodata. This is heuristic, sadly, since I can't think
- # of another way of doing it.
- moved_late_rodata = {}
- if any(all_late_rodata_dummy_bytes) or any(all_jtbl_rodata_size):
- source = asm_objfile.find_section('.rodata', 0)
- target = objfile.find_section('.rodata', 0)
- source_pos = asm_objfile.symtab.find_symbol_in_section(late_rodata_source_name_start, source)
- source_end = asm_objfile.symtab.find_symbol_in_section(late_rodata_source_name_end, source)
- if source_end - source_pos != sum(map(len, all_late_rodata_dummy_bytes)) * 2 + sum(all_jtbl_rodata_size):
- raise Failure("computed wrong size of .late_rodata")
- new_data = list(target.data)
- for dummy_bytes_list, jtbl_rodata_size in zip(all_late_rodata_dummy_bytes, all_jtbl_rodata_size):
- for index, dummy_bytes in enumerate(dummy_bytes_list):
- pos = target.data.index(dummy_bytes, last_rodata_pos)
- # This check is nice, but makes time complexity worse for large files:
- if SLOW_CHECKS and target.data.find(dummy_bytes, pos + 2) != -1:
- raise Failure("multiple occurrences of late_rodata hex magic. Change asm-processor to use something better than 0xE0123456!")
- if index == 0 and len(dummy_bytes_list) > 1 and target.data[pos+2:pos+8] == b'\0\0\0\0':
- # Ugly hack to handle double alignment for non-matching builds.
- # We were told by .late_rodata_alignment (or deduced from a .double)
- # that a function's late_rodata started out 4 (mod 8), and emitted
- # a float and then a double. But it was actually 0 (mod 8), so our
- # double was moved by 4 bytes. To make them adjacent to keep jump
- # tables correct, move the float by 4 bytes as well.
- new_data[pos:pos+2] = b'\0\0\0\0'
- pos += 2
- new_data[pos:pos+2] = source.data[source_pos:source_pos+2]
- moved_late_rodata[source_pos] = pos
- last_rodata_pos = pos + 2
- source_pos += 2
- if jtbl_rodata_size > 0:
- assert dummy_bytes_list, "should always have dummy bytes before jtbl data"
- pos = last_rodata_pos
- new_data[pos : pos + jtbl_rodata_size] = \
- source.data[source_pos : source_pos + jtbl_rodata_size]
- for i in range(0, jtbl_rodata_size, 2):
- moved_late_rodata[source_pos + i] = pos + i
- jtbl_rodata_positions.add(pos + i)
- last_rodata_pos += jtbl_rodata_size
- source_pos += jtbl_rodata_size
- target.data = bytes(new_data)
-
- # Merge strtab data.
- strtab_adj = len(objfile.symtab.strtab.data)
- objfile.symtab.strtab.data += asm_objfile.symtab.strtab.data
-
- # Find relocated symbols
- relocated_symbols = set()
- for obj in [asm_objfile, objfile]:
- for sec in obj.sections:
- for reltab in sec.relocated_by:
- for rel in reltab.relocations:
- relocated_symbols.add(obj.symtab.symbol_entries[rel.sym_index])
-
- # Move over symbols, deleting the temporary function labels.
- # Sometimes this naive procedure results in duplicate symbols, or UNDEF
- # symbols that are also defined the same .o file. Hopefully that's fine.
- # Skip over local symbols that aren't used relocated against, to avoid
- # conflicts.
- new_local_syms = [s for s in objfile.symtab.local_symbols() if not is_temp_name(s.name)]
- new_global_syms = [s for s in objfile.symtab.global_symbols() if not is_temp_name(s.name)]
- n_text = 0
- for i, s in enumerate(asm_objfile.symtab.symbol_entries):
- is_local = (i < asm_objfile.symtab.sh_info)
- if is_local and s not in relocated_symbols:
- continue
- if is_temp_name(s.name):
- continue
- if s.st_shndx not in [SHN_UNDEF, SHN_ABS]:
- section_name = asm_objfile.sections[s.st_shndx].name
- if section_name not in SECTIONS:
- raise Failure("generated assembly .o must only have symbols for .text, .data, .rodata, .sdata, .sdata2, .sbss, ABS and UNDEF, but found " + section_name)
- if section_name == '.sbss2': #! I'm not sure why this isn't working
- continue
- s.st_shndx = objfile.find_section(section_name, n_text if section_name == '.text' else 0).index
- if section_name == '.text':
- n_text += 1
- # glabel's aren't marked as functions, making objdump output confusing. Fix that.
- if s.name in all_text_glabels:
- s.type = STT_FUNC
- if objfile.sections[s.st_shndx].name == '.rodata' and s.st_value in moved_late_rodata:
- s.st_value = moved_late_rodata[s.st_value]
- s.st_name += strtab_adj
- if is_local:
- new_local_syms.append(s)
- else:
- new_global_syms.append(s)
- new_syms = new_local_syms + new_global_syms
- for i, s in enumerate(new_syms):
- s.new_index = i
- objfile.symtab.data = b''.join(s.to_bin() for s in new_syms)
- objfile.symtab.sh_info = len(new_local_syms)
-
- # Move over relocations
- n_text = 0
- for sec in objfile.sections:
- sectype = sec.name
- # This should work as long as you NONMATCH whole functions rather than asm fragments
- target = objfile.find_section(sectype, n_text if sectype == '.text' else 0)
-
- if target is not None:
- # fixup relocation symbol indices, since we butchered them above
- for reltab in target.relocated_by:
- nrels = []
- for rel in reltab.relocations:
- if (sectype == '.text' and rel.r_offset in modified_text_positions or
- sectype == '.rodata' and rel.r_offset in jtbl_rodata_positions) or sectype == ".sbss2":
- # don't include relocations for late_rodata dummy code
- continue
- # hopefully we don't have relocations for local or
- # temporary symbols, so new_index exists
- rel.sym_index = objfile.symtab.symbol_entries[rel.sym_index].new_index
- nrels.append(rel)
- reltab.relocations = nrels
- reltab.data = b''.join(rel.to_bin() for rel in nrels)
-
- if not to_copy[sectype + (str(n_text) if sectype == '.text' else '')]:
- if sectype == '.text':
- n_text += 1
- continue
-
- func = to_copy[sectype + str(n_text) if sectype == '.text' else ''][0][2]
- asm_n_text = asm_objfile.text_section_index(func + '_asm_start')
- source = asm_objfile.find_section(sectype, asm_n_text if sectype == '.text' else 0)
- if not source:
- if sectype == '.text':
- n_text += 1
- continue
-
- target_reltab = objfile.find_section('.rel' + sectype, n_text if sectype == '.text' else 0)
- target_reltaba = objfile.find_section('.rela' + sectype, n_text if sectype == '.text' else 0)
- for reltab in source.relocated_by:
- for rel in reltab.relocations:
- rel.sym_index = asm_objfile.symtab.symbol_entries[rel.sym_index].new_index
- # I suspect that this is requried for matching. If the after linking the
- # binary doesn't match, retry after commenting out the following line:
- rel.r_addend = 0
- if sectype == '.rodata' and rel.r_offset in moved_late_rodata:
- rel.r_offset = moved_late_rodata[rel.r_offset]
- new_data = b''.join(rel.to_bin() for rel in reltab.relocations)
- if reltab.sh_type == SHT_REL:
- target_reltab = objfile.add_section('.rel' + sectype,
- sh_type=SHT_REL, sh_flags=0,
- sh_link=objfile.symtab.index, sh_info=target.index,
- sh_addralign=4, sh_entsize=8, data=b'')
- target_reltab.data += new_data
- else:
- # Always append as a separate .rela.text section
- target_reltaba = objfile.add_section('.rela' + sectype,
- sh_type=SHT_RELA, sh_flags=0,
- sh_link=objfile.symtab.index, sh_info=target.index,
- sh_addralign=4, sh_entsize=12, data=b'')
- target_reltaba.data += new_data
- if sectype == '.text':
- n_text += 1
-
- objfile.write(objfile_name)
- finally:
- s_file.close()
- #os.remove(s_name)
- try:
- pass
- #os.remove(o_name)
- except:
- pass
-
-def run_wrapped(argv, outfile):
- parser = argparse.ArgumentParser(description="Pre-process .c files and post-process .o files to enable embedding assembly into C.")
- parser.add_argument('filename', help="path to .c code")
- parser.add_argument('--post-process', dest='objfile', help="path to .o file to post-process")
- parser.add_argument('--assembler', dest='assembler', help="assembler command (e.g. \"mips-linux-gnu-as -march=vr4300 -mabi=32\")")
- parser.add_argument('--asm-prelude', dest='asm_prelude', help="path to a file containing a prelude to the assembly file (with .set and .macro directives, e.g.)")
- parser.add_argument('--input-enc', default='latin1', help="Input encoding (default: latin1)")
- parser.add_argument('--output-enc', default='latin1', help="Output encoding (default: latin1)")
- parser.add_argument('-framepointer', dest='framepointer', action='store_true')
- parser.add_argument('-g3', dest='g3', action='store_true')
- group = parser.add_mutually_exclusive_group(required=False)
- group.add_argument('-O1', dest='opt', action='store_const', const='O1')
- group.add_argument('-O2', dest='opt', action='store_const', const='O2')
- group.add_argument('-g', dest='opt', action='store_const', const='g')
- args = parser.parse_args(argv)
- opt = args.opt
- if args.g3:
- if opt != 'O2':
- raise Failure("-g3 is only supported together with -O2")
- opt = 'g3'
-
- if args.objfile is None:
- with open(args.filename, encoding=args.input_enc) as f:
- parse_source(f, opt=opt, framepointer=args.framepointer, input_enc=args.input_enc, output_enc=args.output_enc, print_source=outfile)
- else:
- if args.assembler is None:
- raise Failure("must pass assembler command")
- with open(args.filename, encoding=args.input_enc) as f:
- functions = parse_source(f, opt=opt, framepointer=args.framepointer, input_enc=args.input_enc, output_enc=args.output_enc)
- if not functions:
- return
- asm_prelude = b''
- if args.asm_prelude:
- with open(args.asm_prelude, 'rb') as f:
- asm_prelude = f.read()
- fixup_objfile(args.objfile, functions, asm_prelude, args.assembler, args.output_enc)
-
-def run(argv, outfile=sys.stdout.buffer):
- try:
- run_wrapped(argv, outfile)
- except Failure as e:
- sys.exit(1)
-
-if __name__ == "__main__":
- run(sys.argv[1:])
+#!/usr/bin/env python3 +import argparse +import tempfile +import struct +import copy +import sys +import re +import os +from collections import namedtuple, defaultdict +from io import StringIO + +MAX_FN_SIZE = 100 +SLOW_CHECKS = False + +EI_NIDENT = 16 +EI_CLASS = 4 +EI_DATA = 5 +EI_VERSION = 6 +EI_OSABI = 7 +EI_ABIVERSION = 8 +STN_UNDEF = 0 + +SHN_UNDEF = 0 +SHN_ABS = 0xfff1 +SHN_COMMON = 0xfff2 +SHN_XINDEX = 0xffff +SHN_LORESERVE = 0xff00 + +STT_NOTYPE = 0 +STT_OBJECT = 1 +STT_FUNC = 2 +STT_SECTION = 3 +STT_FILE = 4 +STT_COMMON = 5 +STT_TLS = 6 + +STB_LOCAL = 0 +STB_GLOBAL = 1 +STB_WEAK = 2 + +STV_DEFAULT = 0 +STV_INTERNAL = 1 +STV_HIDDEN = 2 +STV_PROTECTED = 3 + +SHT_NULL = 0 +SHT_PROGBITS = 1 +SHT_SYMTAB = 2 +SHT_STRTAB = 3 +SHT_RELA = 4 +SHT_HASH = 5 +SHT_DYNAMIC = 6 +SHT_NOTE = 7 +SHT_NOBITS = 8 +SHT_REL = 9 +SHT_SHLIB = 10 +SHT_DYNSYM = 11 +SHT_INIT_ARRAY = 14 +SHT_FINI_ARRAY = 15 +SHT_PREINIT_ARRAY = 16 +SHT_GROUP = 17 +SHT_SYMTAB_SHNDX = 18 +SHT_MIPS_GPTAB = 0x70000003 +SHT_MIPS_DEBUG = 0x70000005 +SHT_MIPS_REGINFO = 0x70000006 +SHT_MIPS_OPTIONS = 0x7000000d + +SHF_WRITE = 0x1 +SHF_ALLOC = 0x2 +SHF_EXECINSTR = 0x4 +SHF_MERGE = 0x10 +SHF_STRINGS = 0x20 +SHF_INFO_LINK = 0x40 +SHF_LINK_ORDER = 0x80 +SHF_OS_NONCONFORMING = 0x100 +SHF_GROUP = 0x200 +SHF_TLS = 0x400 + +R_MIPS_32 = 2 +R_MIPS_26 = 4 +R_MIPS_HI16 = 5 +R_MIPS_LO16 = 6 + + +class ElfHeader: + """ + typedef struct { + unsigned char e_ident[EI_NIDENT]; + Elf32_Half e_type; + Elf32_Half e_machine; + Elf32_Word e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; + } Elf32_Ehdr; + """ + + def __init__(self, data): + self.e_ident = data[:EI_NIDENT] + self.e_type, self.e_machine, self.e_version, self.e_entry, self.e_phoff, self.e_shoff, self.e_flags, self.e_ehsize, self.e_phentsize, self.e_phnum, self.e_shentsize, self.e_shnum, self.e_shstrndx = struct.unpack('<HHIIIIIHHHHHH', data[EI_NIDENT:]) + assert self.e_ident[EI_CLASS] == 1 # 32-bit + #assert self.e_ident[EI_DATA] == 2 # big-endian + #assert self.e_type == 1 # relocatable + #assert self.e_machine == 8 # MIPS I Architecture + assert self.e_phoff == 0 # no program header + assert self.e_shoff != 0 # section header + assert self.e_shstrndx != SHN_UNDEF + + def to_bin(self): + return self.e_ident + struct.pack('<HHIIIIIHHHHHH', self.e_type, + self.e_machine, self.e_version, self.e_entry, self.e_phoff, + self.e_shoff, self.e_flags, self.e_ehsize, self.e_phentsize, + self.e_phnum, self.e_shentsize, self.e_shnum, self.e_shstrndx) + + +class Symbol: + """ + typedef struct { + Elf32_Word st_name; + Elf32_Addr st_value; + Elf32_Word st_size; + unsigned char st_info; + unsigned char st_other; + Elf32_Half st_shndx; + } Elf32_Sym; + """ + + def __init__(self, data, strtab): + self.st_name, self.st_value, self.st_size, st_info, self.st_other, self.st_shndx = struct.unpack('<IIIBBH', data) + assert self.st_shndx != SHN_XINDEX, "too many sections (SHN_XINDEX not supported)" + self.bind = st_info >> 4 + self.type = st_info & 15 + self.name = strtab.lookup_str(self.st_name) + self.visibility = self.st_other & 3 + + def to_bin(self): + st_info = (self.bind << 4) | self.type + return struct.pack('<IIIBBH', self.st_name, self.st_value, self.st_size, st_info, self.st_other, self.st_shndx) + + +class Relocation: + def __init__(self, data, sh_type): + self.sh_type = sh_type + if sh_type == SHT_REL: + self.r_offset, self.r_info = struct.unpack('<II', data) + else: + self.r_offset, self.r_info, self.r_addend = struct.unpack('<III', data) + self.sym_index = self.r_info >> 8 + self.rel_type = self.r_info & 0xff + + def to_bin(self): + self.r_info = (self.sym_index << 8) | self.rel_type + if self.sh_type == SHT_REL: + return struct.pack('<II', self.r_offset, self.r_info) + else: + return struct.pack('<III', self.r_offset, self.r_info, self.r_addend) + +class Section: + """ + typedef struct { + Elf32_Word sh_name; + Elf32_Word sh_type; + Elf32_Word sh_flags; + Elf32_Addr sh_addr; + Elf32_Off sh_offset; + Elf32_Word sh_size; + Elf32_Word sh_link; + Elf32_Word sh_info; + Elf32_Word sh_addralign; + Elf32_Word sh_entsize; + } Elf32_Shdr; + """ + + def __init__(self, header, data, index): + self.sh_name, self.sh_type, self.sh_flags, self.sh_addr, self.sh_offset, self.sh_size, self.sh_link, self.sh_info, self.sh_addralign, self.sh_entsize = struct.unpack('<IIIIIIIIII', header) + assert not self.sh_flags & SHF_LINK_ORDER + if self.sh_entsize != 0: + assert self.sh_size % self.sh_entsize == 0 + if self.sh_type == SHT_NOBITS: + self.data = '' + else: + self.data = data[self.sh_offset:self.sh_offset + self.sh_size] + self.index = index + self.relocated_by = [] + + @staticmethod + def from_parts(sh_name, sh_type, sh_flags, sh_link, sh_info, sh_addralign, sh_entsize, data, index): + header = struct.pack('<IIIIIIIIII', sh_name, sh_type, sh_flags, 0, 0, len(data), sh_link, sh_info, sh_addralign, sh_entsize) + return Section(header, data, index) + + def lookup_str(self, index): + assert self.sh_type == SHT_STRTAB + to = self.data.find(b'\0', index) + assert to != -1 + return self.data[index:to].decode('latin1') + + def add_str(self, string): + assert self.sh_type == SHT_STRTAB + ret = len(self.data) + self.data += string.encode('latin1') + b'\0' + return ret + + def is_rel(self): + return self.sh_type == SHT_REL or self.sh_type == SHT_RELA + + def header_to_bin(self): + if self.sh_type != SHT_NOBITS: + self.sh_size = len(self.data) + return struct.pack('<IIIIIIIIII', self.sh_name, self.sh_type, self.sh_flags, self.sh_addr, self.sh_offset, self.sh_size, self.sh_link, self.sh_info, self.sh_addralign, self.sh_entsize) + + def late_init(self, sections): + if self.sh_type == SHT_SYMTAB: + self.init_symbols(sections) + elif self.is_rel(): + self.rel_target = sections[self.sh_info] + self.rel_target.relocated_by.append(self) + self.init_relocs() + + def find_symbol(self, name): + assert self.sh_type == SHT_SYMTAB + for s in self.symbol_entries: + if s.name == name: + return (s.st_shndx, s.st_value) + return None + + def find_symbol_in_section(self, name, section): + pos = self.find_symbol(name) + assert pos is not None + assert pos[0] == section.index + return pos[1] + + def init_symbols(self, sections): + assert self.sh_type == SHT_SYMTAB + assert self.sh_entsize == 16 + self.strtab = sections[self.sh_link] + entries = [] + for i in range(0, self.sh_size, self.sh_entsize): + entries.append(Symbol(self.data[i:i+self.sh_entsize], self.strtab)) + self.symbol_entries = entries + + def init_relocs(self): + assert self.is_rel() + entries = [] + for i in range(0, self.sh_size, self.sh_entsize): + entries.append(Relocation(self.data[i:i+self.sh_entsize], self.sh_type)) + self.relocations = entries + + def local_symbols(self): + assert self.sh_type == SHT_SYMTAB + return self.symbol_entries[:self.sh_info] + + def global_symbols(self): + assert self.sh_type == SHT_SYMTAB + return self.symbol_entries[self.sh_info:] + + +class ElfFile: + def __init__(self, data): + self.data = data + assert data[:4] == b'\x7fELF', "not an ELF file" + + self.elf_header = ElfHeader(data[0:52]) + + offset, size = self.elf_header.e_shoff, self.elf_header.e_shentsize + null_section = Section(data[offset:offset + size], data, 0) + num_sections = self.elf_header.e_shnum or null_section.sh_size + + self.sections = [null_section] + for i in range(1, num_sections): + ind = offset + i * size + self.sections.append(Section(data[ind:ind + size], data, i)) + + symtab = None + for s in self.sections: + if s.sh_type == SHT_SYMTAB: + assert not symtab + symtab = s + assert symtab is not None + self.symtab = symtab + + shstr = self.sections[self.elf_header.e_shstrndx] + for s in self.sections: + s.name = shstr.lookup_str(s.sh_name) + s.late_init(self.sections) + + def find_section(self, name, num): + i = 0 # Count how many sections of name `name` have been encountered so far, when i reaches `num` return that section + for s in self.sections: + if s.name == name and i == num: + return s + # Increment if section is a .text section + if s.name == ".text": + i += 1 + return None + + # Because Metrowerks for DS can make duplicate .text sections + # for every function, we may need to lookup a specific .text area. + def find_section_with_name(self, name, st_name): + for s in self.sections: + if s.name == name and s.sh_name == st_name: + return s + return None + + # Return i, where i is the ith text section corresponding to the function + # called `name`. + def text_section_index(self, name): + st_shndx, _ = self.symtab.find_symbol(name) + n_text = 0 + for sec in self.sections: + if sec.index == st_shndx: + return n_text + if sec.name =='.text': + n_text += 1 + return -1 + + def add_section(self, name, sh_type, sh_flags, sh_link, sh_info, sh_addralign, sh_entsize, data): + shstr = self.sections[self.elf_header.e_shstrndx] + sh_name = shstr.add_str(name) + s = Section.from_parts(sh_name=sh_name, sh_type=sh_type, + sh_flags=sh_flags, sh_link=sh_link, sh_info=sh_info, + sh_addralign=sh_addralign, sh_entsize=sh_entsize, data=data, + index=len(self.sections)) + self.sections.append(s) + s.name = name + s.late_init(self.sections) + return s + + def drop_irrelevant_sections(self): + # We can only drop sections at the end, since otherwise section + # references might be wrong. Luckily, these sections typically are. + while self.sections[-1].sh_type in [SHT_MIPS_DEBUG, SHT_MIPS_GPTAB]: + self.sections.pop() + + def write(self, filename): + outfile = open(filename, 'wb') + outidx = 0 + def write_out(data): + nonlocal outidx + outfile.write(data) + outidx += len(data) + def pad_out(align): + if align and outidx % align: + write_out(b'\0' * (align - outidx % align)) + + self.elf_header.e_shnum = len(self.sections) + write_out(self.elf_header.to_bin()) + + for s in self.sections: + if s.sh_type != SHT_NOBITS and s.sh_type != SHT_NULL: + pad_out(s.sh_addralign) + s.sh_offset = outidx + write_out(s.data) + + pad_out(4) + self.elf_header.e_shoff = outidx + for s in self.sections: + write_out(s.header_to_bin()) + + outfile.seek(0) + outfile.write(self.elf_header.to_bin()) + outfile.close() + + +def is_temp_name(name): + return name.startswith('_asmpp_') + + +# https://stackoverflow.com/a/241506 +def re_comment_replacer(match): + s = match.group(0) + if s[0] in "/#": + return " " + else: + return s + + +re_comment_or_string = re.compile( + r'#.*|/\*.*?\*/|"(?:\\.|[^\\"])*"' +) + + +class Failure(Exception): + def __init__(self, message): + self.message = message + + def __str__(self): + return self.message + + +class GlobalState: + def __init__(self, min_instr_count, skip_instr_count, use_jtbl_for_rodata): + # A value that hopefully never appears as a 32-bit rodata constant (or we + # miscompile late rodata). Increases by 1 in each step. + self.late_rodata_hex = 0xE0123456 + self.namectr = 0 + self.min_instr_count = min_instr_count + self.skip_instr_count = skip_instr_count + self.use_jtbl_for_rodata = use_jtbl_for_rodata + + def next_late_rodata_hex(self): + dummy_bytes = struct.pack('<I', self.late_rodata_hex) + if (self.late_rodata_hex & 0xffff) == 0: + # Avoid lui + self.late_rodata_hex += 1 + self.late_rodata_hex += 1 + return dummy_bytes + + def make_name(self, cat): + self.namectr += 1 + return '_asmpp_{}{}'.format(cat, self.namectr) + + +Function = namedtuple('Function', ['text_glabels', 'asm_conts', 'late_rodata_dummy_bytes', 'jtbl_rodata_size', 'late_rodata_asm_conts', 'fn_desc', 'data']) + + +class GlobalAsmBlock: + def __init__(self, fn_desc): + self.fn_desc = fn_desc + self.cur_section = '.text' + self.asm_conts = [] + self.late_rodata_asm_conts = [] + self.late_rodata_alignment = 0 + self.late_rodata_alignment_from_content = False + self.text_glabels = [] + self.fn_section_sizes = { + '.text': 0, + '.init': 0, + '.data': 0, + '.bss': 0, + '.rodata': 0, + '.sdata': 0, + '.sdata2': 0, + '.sbss': 0, + #'.sbss2': 0, + '.late_rodata': 0, + } + self.fn_ins_inds = [] + self.glued_line = '' + self.num_lines = 0 + + def fail(self, message, line=None): + context = self.fn_desc + if line: + context += ", at line \"" + line + "\"" + raise Failure(message + "\nwithin " + context) + + def count_quoted_size(self, line, z, real_line, output_enc): + line = line.encode(output_enc).decode('latin1') + in_quote = False + num_parts = 0 + ret = 0 + i = 0 + digits = "0123456789" # 0-7 would be more sane, but this matches GNU as + while i < len(line): + c = line[i] + i += 1 + if not in_quote: + if c == '"': + in_quote = True + num_parts += 1 + else: + if c == '"': + in_quote = False + continue + ret += 1 + if c != '\\': + continue + if i == len(line): + self.fail("backslash at end of line not supported", real_line) + c = line[i] + i += 1 + # (if c is in "bfnrtv", we have a real escaped literal) + if c == 'x': + # hex literal, consume any number of hex chars, possibly none + while i < len(line) and line[i] in digits + "abcdefABCDEF": + i += 1 + elif c in digits: + # octal literal, consume up to two more digits + it = 0 + while i < len(line) and line[i] in digits and it < 2: + i += 1 + it += 1 + + if in_quote: + self.fail("unterminated string literal", real_line) + if num_parts == 0: + self.fail(".ascii with no string", real_line) + return ret + num_parts if z else ret + + + def align4(self): + while self.fn_section_sizes[self.cur_section] % 2 != 0: + self.fn_section_sizes[self.cur_section] += 1 + + def add_sized(self, size, line): + if self.cur_section in ['.text', '.init', '.late_rodata']: + if size % 2 != 0: + self.fail("size must be a multiple of 2 or 4", line) + if size < 0: + self.fail("size cannot be negative", line) + self.fn_section_sizes[self.cur_section] += size + if self.cur_section in ['.text', '.init']: + if not self.text_glabels: + self.fail(".text or .init block without an initial glabel", line) + self.fn_ins_inds.append((self.num_lines - 1, size // 2)) + + def process_line(self, line, output_enc): + self.num_lines += 1 + if line.endswith('\\'): + self.glued_line += line[:-1] + return + line = self.glued_line + line + self.glued_line = '' + + real_line = line + line = re.sub(re_comment_or_string, re_comment_replacer, line) + line = line.strip() + line = re.sub(r'^[a-zA-Z0-9_]+:\s*', '', line) + changed_section = False + emitting_double = False + if line.startswith('glabel ') and self.cur_section in ['.text', '.init']: + self.text_glabels.append(line.split()[1]) + if not line: + pass # empty line + elif line.startswith('glabel ') or (' ' not in line and line.endswith(':')): + pass # label + elif line.startswith('.section') or line in ['.text', '.init', '.data', '.rdata', '.rodata', '.sdata', '.sdata2', '.bss','.sbss', '.late_rodata']: + # section change + self.cur_section = '.rodata' if line == '.rdata' else line.split(',')[0].split()[-1] + if self.cur_section not in ['.data', '.text', '.init', '.rodata', '.sdata', '.sdata2', '.late_rodata', '.bss', '.sbss']: + self.fail("unrecognized .section directive", real_line) + changed_section = True + elif line.startswith('.late_rodata_alignment'): + if self.cur_section != '.late_rodata': + self.fail(".late_rodata_alignment must occur within .late_rodata section", real_line) + value = int(line.split()[1]) + if value not in [4, 8]: + self.fail(".late_rodata_alignment argument must be 4 or 8", real_line) + if self.late_rodata_alignment and self.late_rodata_alignment != value: + self.fail(".late_rodata_alignment alignment assumption conflicts with earlier .double directive. Make sure to provide explicit alignment padding.") + self.late_rodata_alignment = value + changed_section = True + elif line.startswith('.incbin'): + self.add_sized(int(line.split(',')[-1].strip(), 0), real_line) + elif line.startswith('.skip'): + self.add_sized(int(line.split()[-1].strip(), 0), real_line) + elif line.startswith('.long') or line.startswith('.float'): + self.align4() + self.add_sized(4 * len(line.split(',')), real_line) + elif line.startswith('.double'): + self.align4() + if self.cur_section == '.late_rodata': + align8 = self.fn_section_sizes[self.cur_section] % 8 + # Automatically set late_rodata_alignment, so the generated C code uses doubles. + # This gives us correct alignment for the transferred doubles even when the + # late_rodata_alignment is wrong, e.g. for non-matching compilation. + if not self.late_rodata_alignment: + self.late_rodata_alignment = 8 - align8 + self.late_rodata_alignment_from_content = True + elif self.late_rodata_alignment != 8 - align8: + if self.late_rodata_alignment_from_content: + self.fail("found two .double directives with different start addresses mod 8. Make sure to provide explicit alignment padding.", real_line) + else: + self.fail(".double at address that is not 0 mod 8 (based on .late_rodata_alignment assumption). Make sure to provide explicit alignment padding.", real_line) + self.add_sized(8 * len(line.split(',')), real_line) + emitting_double = True + elif line.startswith('.space'): + self.add_sized(int(line.split()[1], 0), real_line) + elif line.startswith('.balign') or line.startswith('.align'): + align = int(line.split()[1]) + if align != 4: + self.fail("only .balign 4 is supported", real_line) + self.align4() + elif line.startswith('.asci'): + z = (line.startswith('.asciz') or line.startswith('.asciiz')) + self.add_sized(self.count_quoted_size(line, z, real_line, output_enc), real_line) + elif line.startswith('.byte'): + self.add_sized(len(line.split(',')), real_line) + # Branches are 4 bytes long + elif line.startswith('bl'): + self.add_sized(4, real_line) + else: + # Unfortunately, macros are hard to support for .rodata -- + # we don't know how how space they will expand to before + # running the assembler, but we need that information to + # construct the C code. So if we need that we'll either + # need to run the assembler twice (at least in some rare + # cases), or change how this program is invoked. + # Similarly, we can't currently deal with pseudo-instructions + # that expand to several real instructions. + if self.cur_section != '.text' and self.cur_section != '.init': + self.fail("instruction or macro call in non-.text/.init section? not supported", real_line) + self.add_sized(2, real_line) + if self.cur_section == '.late_rodata': + if not changed_section: + if emitting_double: + self.late_rodata_asm_conts.append(".align 0") + self.late_rodata_asm_conts.append(real_line) + if emitting_double: + self.late_rodata_asm_conts.append(".align 2") + else: + self.asm_conts.append(real_line) + + def finish(self, state): + src = [''] * (self.num_lines + 1) + late_rodata_dummy_bytes = [] + jtbl_rodata_size = 0 + late_rodata_fn_output = [] + + num_instr = self.fn_section_sizes['.text'] // 2 + + if self.fn_section_sizes['.late_rodata'] > 0: + # Generate late rodata by emitting unique float constants. + # This requires 3 instructions for each 4 bytes of rodata. + # If we know alignment, we can use doubles, which give 3 + # instructions for 8 bytes of rodata. + size = self.fn_section_sizes['.late_rodata'] // 2 + skip_next = False + needs_double = (self.late_rodata_alignment != 0) + for i in range(size): + if skip_next: + skip_next = False + continue + # Jump tables give 9 instructions for >= 5 words of rodata, and should be + # emitted when: + # - -O2 or -O2 -g3 are used, which give the right codegen + # - we have emitted our first .float/.double (to ensure that we find the + # created rodata in the binary) + # - we have emitted our first .double, if any (to ensure alignment of doubles + # in shifted rodata sections) + # - we have at least 5 words of rodata left to emit (otherwise IDO does not + # generate a jump table) + # - we have at least 10 more instructions to go in this function (otherwise our + # function size computation will be wrong since the delay slot goes unused) + if (not needs_double and state.use_jtbl_for_rodata and i >= 1 and + size - i >= 5 and num_instr - len(late_rodata_fn_output) >= 10): + cases = " ".join("case {}:".format(case) for case in range(size - i)) + late_rodata_fn_output.append("switch (*(volatile int*)0) { " + cases + " ; }") + late_rodata_fn_output.extend([""] * 8) + jtbl_rodata_size = (size - i) * 4 + break + dummy_bytes = state.next_late_rodata_hex() + late_rodata_dummy_bytes.append(dummy_bytes) + if self.late_rodata_alignment == 4 * ((i + 1) % 2 + 1) and i + 1 < size: + dummy_bytes2 = state.next_late_rodata_hex() + late_rodata_dummy_bytes.append(dummy_bytes2) + fval, = struct.unpack('<d', dummy_bytes + dummy_bytes2) + late_rodata_fn_output.append('*(volatile double*)0 = {};'.format(fval)) + skip_next = True + needs_double = True + else: + fval, = struct.unpack('<f', dummy_bytes) + late_rodata_fn_output.append('*(volatile float*)0 = {}f;'.format(fval)) + late_rodata_fn_output.append('') + late_rodata_fn_output.append('') + + text_name = None + if self.fn_section_sizes['.text'] > 0 or late_rodata_fn_output: + text_name = state.make_name('func') + src[0] = 'int {}(void) {{ return '.format(text_name) + instr_count = self.fn_section_sizes['.text'] // 2 + src[self.num_lines] = '((volatile void *) 0); }; ' if instr_count > 1 else '; }; ' + if instr_count < state.min_instr_count: + self.fail("too short .text block") + tot_emitted = 0 + tot_skipped = 0 + fn_emitted = 0 + fn_skipped = 0 + rodata_stack = late_rodata_fn_output[::-1] + for (line, count) in self.fn_ins_inds: + for _ in range(count): + if (fn_emitted > MAX_FN_SIZE and instr_count - tot_emitted > state.min_instr_count and + (not rodata_stack or rodata_stack[-1])): + # Don't let functions become too large. When a function reaches 284 + # instructions, and -O2 -framepointer flags are passed, the IRIX + # compiler decides it is a great idea to start optimizing more. + fn_emitted = 0 + fn_skipped = 0 + src[line] += '((volatile void *) 0); }} int {}(void) {{ return '.format(state.make_name('large_func')) + if fn_skipped < state.skip_instr_count: + fn_skipped += 1 + tot_skipped += 1 + elif rodata_stack: + src[line] += rodata_stack.pop() + else: + src[line] += '*(int *)' + tot_emitted += 1 + fn_emitted += 1 + if rodata_stack: + size = len(late_rodata_fn_output) // 3 + available = instr_count - tot_skipped + self.fail( + "late rodata to text ratio is too high: {} / {} must be <= 1/3\n" + "add .late_rodata_alignment (4|8) to the .late_rodata " + "block to double the allowed ratio." + .format(size, available)) + + init_name = None + if self.fn_section_sizes['.init'] > 0 or late_rodata_fn_output: + init_name = state.make_name('func') + src[0] = 'int {}(void) {{ return '.format(init_name) + instr_count = self.fn_section_sizes['.init'] // 2 + src[self.num_lines] = '((volatile void *) 0); }; ' if instr_count else '; }; ' + if instr_count < state.min_instr_count: + self.fail("too short .init block") + tot_emitted = 0 + tot_skipped = 0 + fn_emitted = 0 + fn_skipped = 0 + rodata_stack = late_rodata_fn_output[::-1] + for (line, count) in self.fn_ins_inds: + for _ in range(count): + if (fn_emitted > MAX_FN_SIZE and instr_count - tot_emitted > state.min_instr_count and + (not rodata_stack or rodata_stack[-1])): + # Don't let functions become too large. When a function reaches 284 + # instructions, and -O2 -framepointer flags are passed, the IRIX + # compiler decides it is a great idea to start optimizing more. + fn_emitted = 0 + fn_skipped = 0 + src[line] += '((volatile void *) 0); }} int {}(void) {{ return '.format(state.make_name('large_func')) + if fn_skipped < state.skip_instr_count: + fn_skipped += 1 + tot_skipped += 1 + elif rodata_stack: + src[line] += rodata_stack.pop() + else: + src[line] += '*(int *)' + tot_emitted += 1 + fn_emitted += 1 + if rodata_stack: + size = len(late_rodata_fn_output) // 3 + available = instr_count - tot_skipped + self.fail( + "late rodata to init ratio is too high: {} / {} must be <= 1/3\n" + "add .late_rodata_alignment (4|8) to the .late_rodata " + "block to double the allowed ratio." + .format(size, available)) + + rodata_name = None + if self.fn_section_sizes['.rodata'] > 0: + rodata_name = state.make_name('rodata') + src[self.num_lines] += f" const char {rodata_name}[{self.fn_section_sizes['.rodata']}] = {{1}};" + + data_name = None + if self.fn_section_sizes['.data'] > 0: + data_name = state.make_name('data') + src[self.num_lines] += f" char {data_name}[{self.fn_section_sizes['.data']}] = {{1}};" + + bss_name = None + if self.fn_section_sizes['.bss'] > 0: + bss_name = state.make_name('bss') + src[self.num_lines] += f" char {bss_name}[{self.fn_section_sizes['.bss']}];" + + sdata_name = None # sdata is like data but small + if self.fn_section_sizes['.sdata'] > 0: + sdata_code = "" + for i in range(self.fn_section_sizes['.sdata']): + sdata_name = state.make_name('sdata') + sdata_code += f" char {sdata_name} = 1;" + src[self.num_lines] += sdata_code + + sdata2_name = None # sdata2 is like rodata but small + if self.fn_section_sizes['.sdata2'] > 0: + sdata2_code = "" + for i in range(self.fn_section_sizes['.sdata2']): + sdata2_name = state.make_name('sdata2') + sdata2_code += f" const char {sdata2_name} = 1;" + src[self.num_lines] += sdata2_code + + sbss_name = None # Similarly, sbss is like uninitialized data but small + if self.fn_section_sizes['.sbss'] > 0: + sbss_code = "" + for i in range(self.fn_section_sizes['.sbss']): + sbss_name = state.make_name('sbss') + sbss_code += f" char {sbss_name};" + src[self.num_lines] += sbss_code + + """ sbss2 is currently borked + sbss2_name = None # Similarly, sbss2 is like uninitialized rodata but small + if self.fn_section_sizes['.sbss2'] > 0: + sbss2_code = "" + for i in range(self.fn_section_sizes['.sbss2']): + sbss2_name = state.make_name('sbss2') + sbss2_code += f" const char {sbss2_name};" + src[self.num_lines] += sbss2_code + """ + + fn = Function( + text_glabels=self.text_glabels, + asm_conts=self.asm_conts, + late_rodata_dummy_bytes=late_rodata_dummy_bytes, + jtbl_rodata_size=jtbl_rodata_size, + late_rodata_asm_conts=self.late_rodata_asm_conts, + fn_desc=self.fn_desc, + data={ + '.text': (text_name, self.fn_section_sizes['.text']), + '.data': (data_name, self.fn_section_sizes['.data']), + '.rodata': (rodata_name, self.fn_section_sizes['.rodata']), + '.bss': (bss_name, self.fn_section_sizes['.bss']), + '.sdata': (sdata_name, self.fn_section_sizes['.sdata']), + '.sdata2': (sdata2_name, self.fn_section_sizes['.sdata2']), + '.sbss': (sbss_name, self.fn_section_sizes['.sbss']), + #'.sbss2': (sbss2_name, self.fn_section_sizes['.sbss2']), + }) + return src, fn + +cutscene_data_regexpr = re.compile(r"CutsceneData (.|\n)*\[\] = {") +float_regexpr = re.compile(r"[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?f") + +def repl_float_hex(m): + return str(struct.unpack("<I", struct.pack("<f", float(m.group(0).strip().rstrip("f"))))[0]) + +def parse_source(f, opt, framepointer, input_enc, output_enc, print_source=None): + opt = "O4" + min_instr_count = 3 # idk + skip_instr_count = 2 # idk + + use_jtbl_for_rodata = False + if opt in ['O2', 'g3'] and not framepointer: + use_jtbl_for_rodata = True + + state = GlobalState(min_instr_count, skip_instr_count, use_jtbl_for_rodata) + + global_asm = None + asm_functions = [] + output_lines = [] + + is_cutscene_data = False + + for line_no, raw_line in enumerate(f, 1): + raw_line = raw_line.rstrip() + line = raw_line.lstrip() + + # Print exactly one output line per source line, to make compiler + # errors have correct line numbers. These will be overridden with + # reasonable content further down. + output_lines.append('') + if global_asm is not None: + if line.startswith(')'): + src, fn = global_asm.finish(state) + for i, line2 in enumerate(src): + output_lines[start_index + i] = line2 + asm_functions.append(fn) + global_asm = None + else: + global_asm.process_line(raw_line, output_enc) + else: + if line in ['GLOBAL_ASM(', '#pragma GLOBAL_ASM(']: + global_asm = GlobalAsmBlock("GLOBAL_ASM block at line " + str(line_no)) + start_index = len(output_lines) + elif ((line.startswith('GLOBAL_ASM("') or line.startswith('#pragma GLOBAL_ASM("')) + and line.endswith('")')): + fname = line[line.index('(') + 2 : -2] + global_asm = GlobalAsmBlock(fname) + with open(fname, encoding=input_enc) as f: + for line2 in f: + global_asm.process_line(line2.rstrip(), output_enc) + src, fn = global_asm.finish(state) + output_lines[-1] = ''.join(src) + asm_functions.append(fn) + global_asm = None + elif ((line.startswith('#include "')) and line.endswith('" EARLY')): + # C includes qualified with EARLY (i.e. #include "file.c" EARLY) will be + # processed recursively when encountered + fpath = os.path.dirname(f.name) + fname = line[line.index(' ') + 2 : -7] + include_src = StringIO() + with open(fpath + os.path.sep + fname, encoding=input_enc) as include_file: + parse_source(include_file, opt, framepointer, input_enc, output_enc, include_src) + output_lines[-1] = include_src.getvalue() + include_src.write('#line ' + str(line_no) + '\n') + include_src.close() + else: + # This is a hack to replace all floating-point numbers in an array of a particular type + # (in this case CutsceneData) with their corresponding IEEE-754 hexadecimal representation + if cutscene_data_regexpr.search(line) is not None: + is_cutscene_data = True + elif line.endswith("};"): + is_cutscene_data = False + if is_cutscene_data: + raw_line = re.sub(float_regexpr, repl_float_hex, raw_line) + output_lines[-1] = raw_line + + if print_source: + if isinstance(print_source, StringIO): + for line in output_lines: + print_source.write(line + '\n') + else: + for line in output_lines: + print_source.write(line.encode(output_enc) + b'\n') + print_source.flush() + if print_source != sys.stdout.buffer: + print_source.close() + out_file = open("output.txt", 'w') + out_file.write(str(asm_functions)) + out_file.close() + return asm_functions + +# Return the function name in objfile corresponding to function +# `asm_func_name` in asm_objfile. `to_copy` is the dictionary of the +# same name in fix_objfile(). +def convert_func_name(asm_func_name, to_copy): + for sec_name, func_data in to_copy.items(): + if func_data and func_data[0][4] == asm_func_name: + return func_data[0][2] + return '' + +def fixup_objfile(objfile_name, functions, asm_prelude, assembler, output_enc): + SECTIONS = ['.data'] + SECTIONS.extend(['.text' for i in range(0,len(functions))]) + SECTIONS.extend(['.rodata', '.bss', '.sdata', '.sdata2', '.sbss']) + + with open(objfile_name, 'rb') as f: + objfile = ElfFile(f.read()) + + prev_locs = defaultdict(int) + to_copy = defaultdict(list) + + asm = [] + all_late_rodata_dummy_bytes = [] + all_jtbl_rodata_size = [] + late_rodata_asm = [] + late_rodata_source_name_start = None + late_rodata_source_name_end = None + + # Generate an assembly file with all the assembly we need to fill in. For + # simplicity we pad with nops/.space so that addresses match exactly, so we + # don't have to fix up relocations/symbol references. + all_text_glabels = set() + for function in functions: + ifdefed = False + for sectype, (temp_name, size) in function.data.items(): + if temp_name is None: + continue + assert size > 0 + n_text = objfile.text_section_index(temp_name) + loc = objfile.symtab.find_symbol(temp_name) + if loc is None: + ifdefed = True + break + loc = loc[1] + prev_loc = prev_locs[sectype + (str(n_text) if sectype == '.text' else '')] + if loc < prev_loc: + raise Failure("Wrongly computed size for section {} (diff {}). This is an asm-processor bug!".format(sectype + (str(n_text) if sectype == '.text' else ''), prev_loc- loc)) + if loc != prev_loc: + asm.append('.section ' + sectype) + if sectype == '.text': + for i in range((loc - prev_loc) // 2): + asm.append('nop') + else: + asm.append('.space {}'.format(loc - prev_loc)) + to_copy[sectype + (str(n_text) if sectype == '.text' else '')].append((loc, size, temp_name, function.fn_desc, function.text_glabels[0])) + prev_locs[sectype + (str(n_text) if sectype == '.text' else '')] = loc + size + if not ifdefed: + all_text_glabels.update(function.text_glabels) + all_late_rodata_dummy_bytes.append(function.late_rodata_dummy_bytes) + all_jtbl_rodata_size.append(function.jtbl_rodata_size) + late_rodata_asm.append(function.late_rodata_asm_conts) + for sectype, (temp_name, size) in function.data.items(): + if temp_name is not None: + asm.append('.section ' + sectype) + asm.append('glabel ' + temp_name + '_asm_start') + asm.append('.section .text') + for line in function.asm_conts: + asm.append(line) + for sectype, (temp_name, size) in function.data.items(): + if temp_name is not None: + #asm.append('.section ' + sectype) + asm.append('glabel ' + temp_name + '_asm_end') + + if any(late_rodata_asm): + late_rodata_source_name_start = '_asmpp_late_rodata_start' + late_rodata_source_name_end = '_asmpp_late_rodata_end' + asm.append('.rdata') + asm.append('glabel {}'.format(late_rodata_source_name_start)) + for conts in late_rodata_asm: + asm.extend(conts) + asm.append('glabel {}'.format(late_rodata_source_name_end)) + + o_file = open("asm_processor_temp.o", 'w').close() # Create temp file. tempfile module isn't working for me. + o_name = "asm_processor_temp.o" + + s_file = open("asm_processor_temp.s", 'wb') # Ditto. + s_name = "asm_processor_temp.s" + try: + s_file.write(asm_prelude + b'\n') + for line in asm: + s_file.write(line.encode(output_enc) + b'\n') + s_file.close() + ret = os.system(assembler + " " + s_name + " -o " + o_name) + if ret != 0: + raise Failure("failed to assemble") + with open(o_name, 'rb') as f: + asm_objfile = ElfFile(f.read()) + + # Remove some clutter from objdump output + objfile.drop_irrelevant_sections() + + """ + # Unify reginfo sections + target_reginfo = objfile.find_section('.reginfo') + source_reginfo_data = list(asm_objfile.find_section('.reginfo').data) + data = list(target_reginfo.data) + for i in range(20): + data[i] |= source_reginfo_data[i] + target_reginfo.data = bytes(data) + """ + + # Move over section contents + modified_text_positions = set() + jtbl_rodata_positions = set() + last_rodata_pos = 0 + n_text = 0 + for sec in objfile.sections: + sectype = sec.name + if not to_copy[sectype + (str(n_text) if sectype == '.text' else '')]: + if sectype == '.text': + n_text += 1 + continue + # This should work as long as you NONMATCH whole functions rather than asm fragments + func = to_copy[sectype + str(n_text) if sectype == '.text' else ''][0][2] + asm_n_text = asm_objfile.text_section_index(func + '_asm_start') + source = asm_objfile.find_section(sectype, asm_n_text if sectype == '.text' else 0) + assert source is not None, "didn't find source section: " + sectype + for (pos, count, temp_name, fn_desc, fn_name) in to_copy[sectype + (str(n_text) if sectype == '.text' else '')]: + loc1 = asm_objfile.symtab.find_symbol_in_section(temp_name + '_asm_start', source) + loc2 = asm_objfile.symtab.find_symbol_in_section(temp_name + '_asm_end', source) + assert loc1 == pos, "assembly and C files don't line up for section " + sectype + ", " + fn_desc + # Since we are nonmatching whole functions, we don't need to insert the correct + # amount of padding into the src file. We don't actually need to insert padding + # at all. We can just plop the asm's text section into the objfile. + # if loc2 - loc1 != count: + # raise Failure("incorrectly computed size for section " + sectype + ", " + fn_desc + ". If using .double, make sure to provide explicit alignment padding.") + if sectype == '.bss' or sectype == '.sbss2': + continue + target = objfile.find_section(sectype, n_text if sectype == '.text' else 0) + assert target is not None, "missing target section of type " + sectype + data = list(target.data) + for (pos, count, _, _, _) in to_copy[sectype + (str(n_text) if sectype == '.text' else '')]: + # mwasmarm 4-aligns text sections, so make sure to copy exactly `count` bytes + data[pos:pos + count] = source.data[pos:pos + count] + if sectype == '.text': + assert count % 2 == 0 + assert pos % 2 == 0 + for i in range(count // 2): + modified_text_positions.add(pos + 2 * i) + elif sectype == '.rodata': + last_rodata_pos = pos + count + target.data = bytes(data) + if sectype == '.text': + n_text += 1 + + # Move over late rodata. This is heuristic, sadly, since I can't think + # of another way of doing it. + moved_late_rodata = {} + if any(all_late_rodata_dummy_bytes) or any(all_jtbl_rodata_size): + source = asm_objfile.find_section('.rodata', 0) + target = objfile.find_section('.rodata', 0) + source_pos = asm_objfile.symtab.find_symbol_in_section(late_rodata_source_name_start, source) + source_end = asm_objfile.symtab.find_symbol_in_section(late_rodata_source_name_end, source) + if source_end - source_pos != sum(map(len, all_late_rodata_dummy_bytes)) * 2 + sum(all_jtbl_rodata_size): + raise Failure("computed wrong size of .late_rodata") + new_data = list(target.data) + for dummy_bytes_list, jtbl_rodata_size in zip(all_late_rodata_dummy_bytes, all_jtbl_rodata_size): + for index, dummy_bytes in enumerate(dummy_bytes_list): + pos = target.data.index(dummy_bytes, last_rodata_pos) + # This check is nice, but makes time complexity worse for large files: + if SLOW_CHECKS and target.data.find(dummy_bytes, pos + 2) != -1: + raise Failure("multiple occurrences of late_rodata hex magic. Change asm-processor to use something better than 0xE0123456!") + if index == 0 and len(dummy_bytes_list) > 1 and target.data[pos+2:pos+8] == b'\0\0\0\0': + # Ugly hack to handle double alignment for non-matching builds. + # We were told by .late_rodata_alignment (or deduced from a .double) + # that a function's late_rodata started out 4 (mod 8), and emitted + # a float and then a double. But it was actually 0 (mod 8), so our + # double was moved by 4 bytes. To make them adjacent to keep jump + # tables correct, move the float by 4 bytes as well. + new_data[pos:pos+2] = b'\0\0\0\0' + pos += 2 + new_data[pos:pos+2] = source.data[source_pos:source_pos+2] + moved_late_rodata[source_pos] = pos + last_rodata_pos = pos + 2 + source_pos += 2 + if jtbl_rodata_size > 0: + assert dummy_bytes_list, "should always have dummy bytes before jtbl data" + pos = last_rodata_pos + new_data[pos : pos + jtbl_rodata_size] = \ + source.data[source_pos : source_pos + jtbl_rodata_size] + for i in range(0, jtbl_rodata_size, 2): + moved_late_rodata[source_pos + i] = pos + i + jtbl_rodata_positions.add(pos + i) + last_rodata_pos += jtbl_rodata_size + source_pos += jtbl_rodata_size + target.data = bytes(new_data) + + # Merge strtab data. + strtab_adj = len(objfile.symtab.strtab.data) + objfile.symtab.strtab.data += asm_objfile.symtab.strtab.data + + # Find relocated symbols + relocated_symbols = set() + for obj in [asm_objfile, objfile]: + for sec in obj.sections: + for reltab in sec.relocated_by: + for rel in reltab.relocations: + relocated_symbols.add(obj.symtab.symbol_entries[rel.sym_index]) + + # Move over symbols, deleting the temporary function labels. + # Sometimes this naive procedure results in duplicate symbols, or UNDEF + # symbols that are also defined the same .o file. Hopefully that's fine. + # Skip over local symbols that aren't used relocated against, to avoid + # conflicts. + new_local_syms = [s for s in objfile.symtab.local_symbols() if not is_temp_name(s.name)] + new_global_syms = [s for s in objfile.symtab.global_symbols() if not is_temp_name(s.name)] + n_text = 0 + for i, s in enumerate(asm_objfile.symtab.symbol_entries): + is_local = (i < asm_objfile.symtab.sh_info) + if is_local and s not in relocated_symbols: + continue + if is_temp_name(s.name): + continue + if s.st_shndx not in [SHN_UNDEF, SHN_ABS]: + section_name = asm_objfile.sections[s.st_shndx].name + if section_name not in SECTIONS: + raise Failure("generated assembly .o must only have symbols for .text, .data, .rodata, .sdata, .sdata2, .sbss, ABS and UNDEF, but found " + section_name) + if section_name == '.sbss2': #! I'm not sure why this isn't working + continue + obj_func_name = convert_func_name(s.name, to_copy) + obj_n_text = objfile.text_section_index(obj_func_name) + s.st_shndx = objfile.find_section(section_name, obj_n_text if section_name == '.text' else 0).index + if section_name == '.text': + n_text += 1 + # glabel's aren't marked as functions, making objdump output confusing. Fix that. + if s.name in all_text_glabels: + s.type = STT_FUNC + if objfile.sections[s.st_shndx].name == '.rodata' and s.st_value in moved_late_rodata: + s.st_value = moved_late_rodata[s.st_value] + s.st_name += strtab_adj + if is_local: + new_local_syms.append(s) + else: + new_global_syms.append(s) + new_syms = new_local_syms + new_global_syms + for i, s in enumerate(new_syms): + s.new_index = i + objfile.symtab.data = b''.join(s.to_bin() for s in new_syms) + objfile.symtab.sh_info = len(new_local_syms) + + # Move over relocations + n_text = 0 + for sec in objfile.sections: + sectype = sec.name + # This should work as long as you NONMATCH whole functions rather than asm fragments + target = objfile.find_section(sectype, n_text if sectype == '.text' else 0) + + if target is not None: + # fixup relocation symbol indices, since we butchered them above + for reltab in target.relocated_by: + nrels = [] + for rel in reltab.relocations: + if (sectype == '.rodata' and rel.r_offset in jtbl_rodata_positions) or sectype == ".sbss2": + # don't include relocations for late_rodata dummy code + continue + # hopefully we don't have relocations for local or + # temporary symbols, so new_index exists + rel.sym_index = objfile.symtab.symbol_entries[rel.sym_index].new_index + nrels.append(rel) + reltab.relocations = nrels + reltab.data = b''.join(rel.to_bin() for rel in nrels) + + if not to_copy[sectype + (str(n_text) if sectype == '.text' else '')]: + if sectype == '.text': + n_text += 1 + continue + + func = to_copy[sectype + str(n_text) if sectype == '.text' else ''][0][2] + asm_n_text = asm_objfile.text_section_index(func + '_asm_start') + source = asm_objfile.find_section(sectype, asm_n_text if sectype == '.text' else 0) + if not source: + if sectype == '.text': + n_text += 1 + continue + + target_reltab = objfile.find_section('.rel' + sectype, n_text if sectype == '.text' else 0) + target_reltaba = objfile.find_section('.rela' + sectype, n_text if sectype == '.text' else 0) + for reltab in source.relocated_by: + for rel in reltab.relocations: + rel.sym_index = asm_objfile.symtab.symbol_entries[rel.sym_index].new_index + if sectype == '.rodata' and rel.r_offset in moved_late_rodata: + rel.r_offset = moved_late_rodata[rel.r_offset] + new_data = b''.join(rel.to_bin() for rel in reltab.relocations) + if reltab.sh_type == SHT_REL: + target_reltab = objfile.add_section('.rel' + sectype, + sh_type=SHT_REL, sh_flags=0, + sh_link=objfile.symtab.index, sh_info=target.index, + sh_addralign=4, sh_entsize=8, data=b'') + target_reltab.data += new_data + else: + # Always append as a separate .rela.text section + target_reltaba = objfile.add_section('.rela' + sectype, + sh_type=SHT_RELA, sh_flags=0, + sh_link=objfile.symtab.index, sh_info=target.index, + sh_addralign=4, sh_entsize=12, data=b'') + target_reltaba.data += new_data + if sectype == '.text': + n_text += 1 + + objfile.write(objfile_name) + finally: + s_file.close() + #os.remove(s_name) + try: + pass + #os.remove(o_name) + except: + pass + +def run_wrapped(argv, outfile): + parser = argparse.ArgumentParser(description="Pre-process .c files and post-process .o files to enable embedding assembly into C.") + parser.add_argument('filename', help="path to .c code") + parser.add_argument('--post-process', dest='objfile', help="path to .o file to post-process") + parser.add_argument('--assembler', dest='assembler', help="assembler command (e.g. \"mips-linux-gnu-as -march=vr4300 -mabi=32\")") + parser.add_argument('--asm-prelude', dest='asm_prelude', help="path to a file containing a prelude to the assembly file (with .set and .macro directives, e.g.)") + parser.add_argument('--input-enc', default='latin1', help="Input encoding (default: latin1)") + parser.add_argument('--output-enc', default='latin1', help="Output encoding (default: latin1)") + parser.add_argument('-framepointer', dest='framepointer', action='store_true') + parser.add_argument('-g3', dest='g3', action='store_true') + group = parser.add_mutually_exclusive_group(required=False) + group.add_argument('-O1', dest='opt', action='store_const', const='O1') + group.add_argument('-O2', dest='opt', action='store_const', const='O2') + group.add_argument('-g', dest='opt', action='store_const', const='g') + args = parser.parse_args(argv) + opt = args.opt + if args.g3: + if opt != 'O2': + raise Failure("-g3 is only supported together with -O2") + opt = 'g3' + + if args.objfile is None: + with open(args.filename, encoding=args.input_enc) as f: + parse_source(f, opt=opt, framepointer=args.framepointer, input_enc=args.input_enc, output_enc=args.output_enc, print_source=outfile) + else: + if args.assembler is None: + raise Failure("must pass assembler command") + with open(args.filename, encoding=args.input_enc) as f: + functions = parse_source(f, opt=opt, framepointer=args.framepointer, input_enc=args.input_enc, output_enc=args.output_enc) + if not functions: + return + asm_prelude = b'' + if args.asm_prelude: + with open(args.asm_prelude, 'rb') as f: + asm_prelude = f.read() + fixup_objfile(args.objfile, functions, asm_prelude, args.assembler, args.output_enc) + +def run(argv, outfile=sys.stdout.buffer): + try: + run_wrapped(argv, outfile) + except Failure as e: + print("Error:", e, file=sys.stderr) + sys.exit(1) + +if __name__ == "__main__": + run(sys.argv[1:]) diff --git a/tools/asm_processor/compile.sh b/tools/asm_processor/compile.sh index 561b0399..feef7fc9 100755 --- a/tools/asm_processor/compile.sh +++ b/tools/asm_processor/compile.sh @@ -9,7 +9,7 @@ PADDED_SRC="$(mktemp --suffix=.c padded-XXXXXX)" PADDED_OBJ="$(mktemp --suffix=.o padded-XXXXXX)" # Create a .c file replacing the nonmatching function with volatile int writes, -# and compile. +# and compile. ../tools/asm_processor/asm_processor.py "$SRC" --assembler "$AS" > "$PADDED_SRC" $CC -c "$PADDED_SRC" -o "$PADDED_OBJ" @@ -20,7 +20,7 @@ cat global.inc >> "$PRELUDE" # Inject the matching assembly into the padded obj file. ../tools/asm_processor/asm_processor.py "$SRC" --post-process "$PADDED_OBJ" --assembler "$AS" --asm-prelude "$PRELUDE" -$DEVKITARM/bin/arm-none-eabi-objcopy --remove-section .comment "$PADDED_OBJ" "$OBJ" +arm-none-eabi-objcopy --remove-section .comment "$PADDED_OBJ" "$OBJ" rm "$PADDED_SRC" rm "$PADDED_OBJ" |