Skip to content

Commit 2f8b564

Browse files
committed
binary: reload disassembly after manual setup
1 parent e660371 commit 2f8b564

7 files changed

Lines changed: 139 additions & 17 deletions

File tree

app/src/main/java/com/kyhsgeekcode/disassembler/models/Architecture.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ object Architecture {
4646
CS_MODE_32
4747
)
4848
MachineType.PPC -> return intArrayOf(CS_ARCH_PPC)
49-
MachineType.PPC64 -> return intArrayOf(CS_ARCH_PPC)
49+
MachineType.PPC64 -> return intArrayOf(CS_ARCH_PPC, CS_MODE_64)
5050
MachineType.S390, MachineType.V800, MachineType.FR20, MachineType.RH32, MachineType.RCE, MachineType.ARM -> return intArrayOf(
5151
CS_ARCH_ARM
5252
)
@@ -57,9 +57,10 @@ object Architecture {
5757
CS_ARCH_X86
5858
)
5959
MachineType.MIPS_X -> return intArrayOf(CS_ARCH_MIPS)
60-
MachineType.COLDFIRE, MachineType.m68HC12, MachineType.MMA, MachineType.PCP, MachineType.NCPU, MachineType.NDR1, MachineType.STARCORE, MachineType.ME16, MachineType.ST100, MachineType.TINYJ, MachineType.x86_64 -> return intArrayOf(
60+
MachineType.COLDFIRE, MachineType.m68HC12, MachineType.MMA, MachineType.PCP, MachineType.NCPU, MachineType.NDR1, MachineType.STARCORE, MachineType.ME16, MachineType.ST100, MachineType.TINYJ -> return intArrayOf(
6161
CS_ARCH_X86
6262
)
63+
MachineType.x86_64 -> return intArrayOf(CS_ARCH_X86, CS_MODE_64)
6364
MachineType.PDSP, MachineType.FX66, MachineType.ST9PLUS, MachineType.ST7, MachineType.m68HC16, MachineType.m68HC11, MachineType.m68HC08, MachineType.m68HC05, MachineType.SVX, MachineType.ST19, MachineType.VAX, MachineType.CRIS, MachineType.JAVELIN, MachineType.FIREPATH, MachineType.ZSP, MachineType.MMIX, MachineType.HUANY, MachineType.PRISM, MachineType.AVR, MachineType.FR30, MachineType.D10V, MachineType.D30V, MachineType.V850, MachineType.M32R, MachineType.MN10300, MachineType.MN10200, MachineType.PJ, MachineType.OPENRISC, MachineType.ARC_A5, MachineType.XTENSA, MachineType.AARCH64 -> return intArrayOf(
6465
CS_ARCH_ARM64
6566
)

app/src/main/java/com/kyhsgeekcode/disassembler/ui/tabs/BinaryOverviewTab.kt

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,35 @@ class BinaryOverviewViewModel {
3131
// val parsedFile: ParsedFile
3232
}
3333

34+
data class BinaryManualSetupConfig(
35+
val codeSectionBase: Long,
36+
val codeSectionLimit: Long,
37+
val entryPoint: Long,
38+
val codeVirtAddr: Long,
39+
val machineType: MachineType
40+
) {
41+
companion object {
42+
fun from(parsedFile: AbstractFile): BinaryManualSetupConfig {
43+
return BinaryManualSetupConfig(
44+
codeSectionBase = parsedFile.codeSectionBase,
45+
codeSectionLimit = parsedFile.codeSectionLimit,
46+
entryPoint = parsedFile.entryPoint,
47+
codeVirtAddr = parsedFile.codeVirtAddr,
48+
machineType = parsedFile.machineType
49+
)
50+
}
51+
}
52+
}
53+
54+
fun requiresDisassemblyReload(
55+
previous: BinaryManualSetupConfig,
56+
current: BinaryManualSetupConfig
57+
): Boolean {
58+
return previous != current
59+
}
60+
3461
@Composable
35-
fun BinaryOverviewTabContent(parsedFile: AbstractFile) {
62+
fun BinaryOverviewTabContent(data: BinaryTabData, parsedFile: AbstractFile) {
3663
var isEnabled by remember { mutableStateOf(false) }
3764
var codeSectionBase by remember { mutableStateOf(parsedFile.codeSectionBase) }
3865
var codeSectionLimit by remember { mutableStateOf(parsedFile.codeSectionLimit) }
@@ -85,11 +112,15 @@ fun BinaryOverviewTabContent(parsedFile: AbstractFile) {
85112
"Entry point out of code section!"
86113
)
87114
if (codeVirtAddr < 0) throw Exception("Virtual address<0")
88-
parsedFile.codeSectionBase = codeSectionBase
89-
parsedFile.codeSectionLimit = codeSectionLimit
90-
parsedFile.entryPoint = entryPoint
91-
parsedFile.codeVirtAddr = codeVirtAddr
92-
parsedFile.machineType = machineType
115+
data.applyManualSetup(
116+
BinaryManualSetupConfig(
117+
codeSectionBase = codeSectionBase,
118+
codeSectionLimit = codeSectionLimit,
119+
entryPoint = entryPoint,
120+
codeVirtAddr = codeVirtAddr,
121+
machineType = machineType
122+
)
123+
)
93124
isEnabled = false
94125
}, enabled = isEnabled) {
95126
Text(stringResource(R.string.finish_setup))

app/src/main/java/com/kyhsgeekcode/disassembler/ui/tabs/BinaryTab.kt

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,20 +89,48 @@ class BinaryTabData(val data: TabKind.Binary, val viewModelScope: CoroutineScope
8989
val abstractFile =
9090
AbstractFile.createInstance(ProjectDataStorage.resolveToRead(data.relPath)!!)
9191
_parsedFile.value = DataResult.Success(abstractFile)
92-
val type = abstractFile.machineType // elf.header.machineType;
92+
disasmData = createPreparedDisasmData(abstractFile)
93+
}
94+
95+
fun applyManualSetup(config: BinaryManualSetupConfig) {
96+
val parsedFileValue = parsedFile.value
97+
val parsedFile = (parsedFileValue as? DataResult.Success)?.data ?: return
98+
val previousConfig = BinaryManualSetupConfig.from(parsedFile)
99+
100+
parsedFile.codeSectionBase = config.codeSectionBase
101+
parsedFile.codeSectionLimit = config.codeSectionLimit
102+
parsedFile.entryPoint = config.entryPoint
103+
parsedFile.codeVirtAddr = config.codeVirtAddr
104+
parsedFile.machineType = config.machineType
105+
106+
if (!requiresDisassemblyReload(previousConfig, config)) {
107+
return
108+
}
109+
110+
viewModelScope.launch {
111+
if (::disasmData.isInitialized) {
112+
MainActivity.Finalize(disasmData.handle)
113+
}
114+
disasmData = createPreparedDisasmData(parsedFile)
115+
}
116+
}
117+
118+
private suspend fun createPreparedDisasmData(file: AbstractFile): BinaryDisasmData {
119+
val type = file.machineType
93120
val archs = Architecture.getArchitecture(type)
94121
val arch = archs[0]
95-
var mode = 0 /*CS_MODE_LITTLE_ENDIAN*/
96-
if (archs.size == 2) mode = archs[1]
122+
var mode = 0
123+
if (archs.size >= 2) {
124+
mode = archs[1]
125+
}
97126
if (arch == Architecture.CS_ARCH_MAX || arch == Architecture.CS_ARCH_ALL) {
98127
throw Exception("No such arch!")
99128
} else {
100129
Timber.d("OK arch")
101130
}
102131

103132
val handle = MainActivity.Open(arch, mode)
104-
disasmData = BinaryDisasmData(abstractFile, handle)
105-
disasmData.prepare()
133+
return BinaryDisasmData(file, handle).also { it.prepare() }
106134
}
107135

108136
inline fun <reified T : BinaryTabKind> setCurrentTab() {
@@ -223,7 +251,7 @@ fun BinaryTabContent(state: Int, data: BinaryTabData, viewModel: MainViewModel)
223251
data
224252
)
225253
is BinaryTabKind.BinaryImportSymbol -> BinaryImportSymbolTabContent(parsedFileValue.data)
226-
is BinaryTabKind.BinaryOverview -> BinaryOverviewTabContent(parsedFileValue.data)
254+
is BinaryTabKind.BinaryOverview -> BinaryOverviewTabContent(data, parsedFileValue.data)
227255
is BinaryTabKind.BinaryString -> TODO()
228256
}
229257
if (isShowJumpToDialog.value) {
@@ -274,5 +302,3 @@ fun BinaryTabContent(state: Int, data: BinaryTabData, viewModel: MainViewModel)
274302

275303

276304
}
277-
278-
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.kyhsgeekcode.disassembler.models
2+
3+
import nl.lxtreme.binutils.elf.MachineType
4+
import kotlin.test.Test
5+
import kotlin.test.assertContentEquals
6+
7+
class ArchitectureTest {
8+
@Test
9+
fun `x86_64 maps to x86 64-bit mode`() {
10+
assertContentEquals(
11+
intArrayOf(Architecture.CS_ARCH_X86, Architecture.CS_MODE_64),
12+
Architecture.getArchitecture(MachineType.x86_64)
13+
)
14+
}
15+
16+
@Test
17+
fun `ppc64 maps to ppc 64-bit mode`() {
18+
assertContentEquals(
19+
intArrayOf(Architecture.CS_ARCH_PPC, Architecture.CS_MODE_64),
20+
Architecture.getArchitecture(MachineType.PPC64)
21+
)
22+
}
23+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.kyhsgeekcode.disassembler.ui.tabs
2+
3+
import nl.lxtreme.binutils.elf.MachineType
4+
import kotlin.test.Test
5+
import kotlin.test.assertFalse
6+
import kotlin.test.assertTrue
7+
8+
class BinaryManualSetupConfigTest {
9+
@Test
10+
fun `requiresDisassemblyReload is false when config is unchanged`() {
11+
val before = BinaryManualSetupConfig(
12+
codeSectionBase = 0x10,
13+
codeSectionLimit = 0x20,
14+
entryPoint = 0x12,
15+
codeVirtAddr = 0x1000,
16+
machineType = MachineType.i386
17+
)
18+
19+
assertFalse(requiresDisassemblyReload(before, before.copy()))
20+
}
21+
22+
@Test
23+
fun `requiresDisassemblyReload is true when any setup field changes`() {
24+
val before = BinaryManualSetupConfig(
25+
codeSectionBase = 0x10,
26+
codeSectionLimit = 0x20,
27+
entryPoint = 0x12,
28+
codeVirtAddr = 0x1000,
29+
machineType = MachineType.i386
30+
)
31+
32+
assertTrue(requiresDisassemblyReload(before, before.copy(machineType = MachineType.x86_64)))
33+
assertTrue(requiresDisassemblyReload(before, before.copy(codeSectionBase = 0x11)))
34+
assertTrue(requiresDisassemblyReload(before, before.copy(codeSectionLimit = 0x21)))
35+
assertTrue(requiresDisassemblyReload(before, before.copy(entryPoint = 0x13)))
36+
assertTrue(requiresDisassemblyReload(before, before.copy(codeVirtAddr = 0x2000)))
37+
}
38+
}

docs/maintenance/backlog-triage.ko.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
| 릴리스 산출물 부재 | `#719` | `planned-fast-follow` | 코드 문제보다 릴리스 파이프라인 문제다 | `#726`와 함께 릴리스 워크플로 정리 |
4343
| 대용량/메모리/RecyclerView 크래시 | `#219`, `#235`, `#442`, `#523` | `planned-fast-follow` | `#728`에서 큰 파일 byte cache 제한과 문자열 검색 결과 상한/stable key를 먼저 넣었다 | `#728` 병합 후 실제 150MB 파일과 긴 문자열 리스트로 재검증하고 나머지 OOM 경로를 분리 |
4444
| 회전/상태 복원 크래시 | `#160` | `covered-by-open-pr` | `#728`에서 Activity 재생성 시 외부 import intent 재처리를 막는 1차 가드를 넣었다 | `#728` 병합 후 실제 회전 회귀를 확인하고 정리 |
45-
| `.so`/ELF/autosetup | `#514`, `#543`, `#576`, `#137` | `planned-fast-follow` | 기능 버그와 기능 요청이 섞여 있다 | `.so` 열기 재현 후 parsing/autosetup을 분리해서 다룬다 |
45+
| `.so`/ELF/autosetup | `#514`, `#543`, `#576`, `#137` | `covered-by-open-pr` | `#728`에서 64-bit ELF machine type 매핑과 override autosetup 재적용 경로를 먼저 수정했다 | `#728` 병합 후 실제 `.so` 샘플로 재검증하고 남는 parser 문제만 분리 |
4646
| crash report 저신호 묶음 | `#716`, `#672`, `#512`, `#508`, `#507`, `#490`, `#438`, `#376`, `#280` | `needs-repro` | 제목만으로는 원인 판단이 어렵고 재현 자료가 부족하다 | 공통 템플릿으로 추가 정보 요청 후 재현 안 되면 정리 |
4747
| SWF 요청 중복 | `#721`, `#112` | `planned-fast-follow` | 같은 방향의 기능 요청이다 | 최신 요청 `#721` 중심으로 정리하고 하나는 중복 처리 검토 |
4848
| 포맷 확장 요청 | `#120`, `#116`, `#124`, `#129` | `planned-fast-follow` | 유효한 확장 요청이지만 기준선 복구 후가 맞다 | 포맷별 난이도와 수요를 다시 평가 |

docs/maintenance/implementation-log.ko.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
| instrumentation test 기반 부재 | AndroidX instrumentation runner를 설정하고 `MainActivity`가 emulator에서 `RESUMED` 상태까지 올라오는 smoke test와 CI emulator job을 추가 | unit test만으로는 잡히지 않는 startup/regression을 GitHub Actions emulator에서 자동 확인할 수 있는 최소 기반을 만들었다 | `app/build.gradle`, `app/src/androidTest/java/com/kyhsgeekcode/disassembler/MainActivitySmokeTest.kt`, `.github/workflows/ci.yml` | 완료 |
2929
| PR 회귀를 잡는 instrumentation 범위 부족 | Compose UI test 의존성, import entry-point test tag, 회전 재생성 smoke test, standard/power-user mode UI 검증 테스트를 추가 | 현재 PR의 핵심 변경인 회전 재생성 경계와 `Select file`/`Advanced import` 노출 규칙을 emulator에서 직접 검증할 수 있게 했다 | `app/build.gradle`, `app/src/main/java/com/kyhsgeekcode/disassembler/ui/MainTab.kt`, `app/src/main/java/com/kyhsgeekcode/disassembler/ui/MainTestTags.kt`, `app/src/androidTest/java/com/kyhsgeekcode/disassembler/MainActivitySmokeTest.kt`, `app/src/androidTest/java/com/kyhsgeekcode/disassembler/MainActivityImportEntryPointStandardModeTest.kt`, `app/src/androidTest/java/com/kyhsgeekcode/disassembler/MainActivityImportEntryPointPowerUserModeTest.kt`, `app/src/androidTest/java/com/kyhsgeekcode/disassembler/PowerUserModePreferenceRule.kt` | 완료 |
3030
| 이슈 `#523`, `#442`, `#235` 대용량 파일/리스트 경계 | 큰 파일은 `ProjectDataStorage` 메모리 캐시에서 제외하고, 문자열 검색 결과는 5,000개로 상한을 두고 stable key로 렌더링하도록 바꿨다 | 150MB 같은 큰 파일을 한 번 열 때 캐시 맵이 오래 붙잡는 메모리 압박을 줄이고, 문자열 탭의 대형 리스트가 계속 불어나면서 Recycler/Lazy list가 불안정해지는 경로를 완화했다 | `app/src/main/java/com/kyhsgeekcode/disassembler/project/ProjectDataStorage.kt`, `app/src/main/java/com/kyhsgeekcode/disassembler/ui/components/TableView.kt`, `app/src/main/java/com/kyhsgeekcode/disassembler/ui/tabs/StringTab.kt`, `app/src/test/java/com/kyhsgeekcode/disassembler/project/ProjectDataStorageCachePolicyTest.kt`, `app/src/test/java/com/kyhsgeekcode/disassembler/ui/tabs/StringSearchResultAccumulatorTest.kt` | 완료 |
31+
| 이슈 `#514`, `#543`, `#576` `.so`/ELF 아키텍처와 override autosetup | `x86_64`, `PPC64` ELF machine type을 64-bit disassembly mode로 매핑하고, binary overview에서 수동 설정을 적용하면 disassembly handle을 다시 열도록 바꿨다 | 일부 `.so`가 32-bit mode로 잘못 열리던 경로를 줄이고, `Override autosetup`이 값만 바꾸고 실제 disassembly는 안 바뀌던 문제를 수정했다 | `app/src/main/java/com/kyhsgeekcode/disassembler/models/Architecture.kt`, `app/src/main/java/com/kyhsgeekcode/disassembler/ui/tabs/BinaryOverviewTab.kt`, `app/src/main/java/com/kyhsgeekcode/disassembler/ui/tabs/BinaryTab.kt`, `app/src/test/java/com/kyhsgeekcode/disassembler/models/ArchitectureTest.kt`, `app/src/test/java/com/kyhsgeekcode/disassembler/ui/tabs/BinaryManualSetupConfigTest.kt` | 완료 |
3132
| 프로젝트 경로/파일명 회귀 방지 | 프로젝트 상대경로 계산과 import 파일명 정규화를 pure helper로 분리 | 단위 테스트가 가능하도록 로직을 분리하고 경계 케이스를 줄였다 | `app/src/main/java/com/kyhsgeekcode/disassembler/project/ProjectManager.kt`, `app/src/main/java/com/kyhsgeekcode/disassembler/viewmodel/MainViewModel.kt` | 완료 |
3233
| 회귀 테스트 부재 | `ProjectManager`, 저장소 권한, Hex 레이아웃, import 파일명 테스트 추가 | 최소한의 유지보수 안전망을 확보했다 | `app/src/test/java/com/kyhsgeekcode/disassembler/ProjectManagerTest.kt`, `app/src/test/java/com/kyhsgeekcode/disassembler/PermissionUtilsTest.kt`, `app/src/test/java/com/kyhsgeekcode/disassembler/ui/components/HexViewLayoutTest.kt`, `app/src/test/java/com/kyhsgeekcode/disassembler/viewmodel/ImportedFileNameTest.kt` | 완료 |
3334

@@ -50,6 +51,8 @@
5051
| 강화된 `assembleDebugAndroidTest` | 통과 | Compose UI 기반 PR 회귀 테스트 세트가 instrumentation APK로 묶이는지 확인 |
5152
| large-file cache policy 테스트 | 통과 | 큰 파일은 메모리 캐시에 남기지 않는 규칙을 고정 |
5253
| string search accumulator 테스트 | 통과 | 문자열 결과 상한과 truncation 동작을 고정 |
54+
| architecture mapping 테스트 | 통과 | `x86_64`, `PPC64`가 64-bit mode로 매핑되는 규칙을 고정 |
55+
| binary manual setup reload 테스트 | 통과 | override autosetup 변경 시 disassembly 재로드 필요 여부를 고정 |
5356
| workflow YAML 파싱 | 통과 | `.github/workflows/ci.yml`, `.github/workflows/release.yml` 모두 Ruby YAML 파서 기준 확인 |
5457

5558
## 다음 웨이브 후보

0 commit comments

Comments
 (0)