ReviewOS

also looking at this

craft-native/craft

perf(android): ship libcraft.so without its DWARF, and keep the DWARF for symbols

#217
Merged glennmichael123 wants to merge fix/android-strip-libcraft into main
6 files +235 -14
scripts/mobile-e2e/protocol.test.tsmodified+60-1
Changes to scripts/mobile-e2e/protocol.test.ts
@@ -1,7 +1,7 @@
11import { describe, expect, it } from 'bun:test'
22import { readFileSync } from 'node:fs'
33import { join } from 'node:path'
4import { ANDROID_DECLINE_PHRASES, androidDeclines, awaitedNeeds, deepLinkProblems, deepLinkResults, DISMISS_SHARE_MENU, evaluateRun, hasTerminated, parseDriverOutput, REQUIRED_CASES, requiredCaseProblems, runtimePermissionGranted, shareMenuInFront, ZIG_REFUSED_ACTIONS, ZIG_SERVED_ACTIONS, ZIG_TESTED_ACTIONS, zigDispatchedActions, zigHandBacks, zigRefusals } from './protocol'
4import { ANDROID_DECLINE_PHRASES, androidDeclines, awaitedNeeds, deepLinkProblems, deepLinkResults, DISMISS_SHARE_MENU, elfSectionNames, evaluateRun, hasTerminated, parseDriverOutput, REQUIRED_CASES, requiredCaseProblems, runtimePermissionGranted, shareMenuInFront, strippedLibraryProblems, ZIG_REFUSED_ACTIONS, ZIG_SERVED_ACTIONS, ZIG_TESTED_ACTIONS, zigDispatchedActions, zigHandBacks, zigRefusals } from './protocol'
55
66const ESC = String.fromCharCode(27)
77
@@ -372,6 +372,65 @@ describe('cold-start deep links', () => {
372372 })
373373})
374374
375/** A minimal ELF64 little-endian file whose section header table names `sections`. */
376function elfWith(sections: string[]): Uint8Array {
377 const names = ['', ...sections, '.shstrtab']
378 const strtab = new TextEncoder().encode(`${names.join('\0')}\0`)
379 const headerSize = 64
380 const entrySize = 64
381 const tableOffset = headerSize + strtab.length
382 const bytes = new Uint8Array(tableOffset + entrySize * names.length)
383 const view = new DataView(bytes.buffer)
384 bytes.set([0x7F, 0x45, 0x4C, 0x46, 2, 1, 1], 0)
385 view.setBigUint64(0x28, BigInt(tableOffset), true)
386 view.setUint16(0x3A, entrySize, true)
387 view.setUint16(0x3C, names.length, true)
388 view.setUint16(0x3E, names.length - 1, true)
389 bytes.set(strtab, headerSize)
390 let nameOffset = 0
391 names.forEach((name, index) => {
392 const at = tableOffset + entrySize * index
393 view.setUint32(at, nameOffset, true)
394 nameOffset += new TextEncoder().encode(name).length + 1
395 if (name === '.shstrtab') {
396 view.setBigUint64(at + 0x18, BigInt(headerSize), true)
397 view.setBigUint64(at + 0x20, BigInt(strtab.length), true)
398 }
399 })
400 return bytes
401}
402
403describe('the shipped Android library', () => {
404 const stripped = ['.dynsym', '.text', '.symtab', '.gnu_debuglink']
405 const debug = ['.debug_info', '.debug_line', '.symtab']
406
407 it('reads section names out of an ELF64 file, and refuses anything else', () => {
408 expect(elfSectionNames(elfWith(['.text', '.debug_info']))).toEqual(['', '.text', '.debug_info', '.shstrtab'])
409 expect(elfSectionNames(new Uint8Array([0x7F, 0x45, 0x4C, 0x46]))).toBeNull()
410 expect(elfSectionNames(new TextEncoder().encode('not an elf file at all, but long enough to have a header, surely'))).toBeNull()
411 })
412
413 it('passes a stripped library with its DWARF moved beside it', () => {
414 expect(strippedLibraryProblems(elfSectionNames(elfWith(stripped)), elfSectionNames(elfWith(debug)))).toEqual([])
415 })
416
417 // #204 as found: the release library still carrying its debug info.
418 it('fails a library that still ships DWARF', () => {
419 const unstripped = elfSectionNames(elfWith(['.dynsym', '.text', '.debug_info', '.debug_str', '.symtab']))
420 expect(strippedLibraryProblems(unstripped, elfSectionNames(elfWith(debug)))).toEqual([
421 'libcraft.so still carries .debug_info, .debug_str; release builds ship without DWARF (#204)',
422 'libcraft.so has no .gnu_debuglink, so nothing ties it to its symbols file',
423 ])
424 })
425
426 it('fails a strip that threw the DWARF away instead of keeping it', () => {
427 expect(strippedLibraryProblems(elfSectionNames(elfWith(stripped)), null))
428 .toEqual(['android-symbols has no libcraft.so.debug beside the stripped library'])
429 expect(strippedLibraryProblems(elfSectionNames(elfWith(stripped)), elfSectionNames(elfWith(['.symtab']))))
430 .toEqual(['libcraft.so.debug holds no .debug_info; the strip discarded the DWARF instead of moving it'])
431 })
432})
433
375434describe('runtime permission state', () => {
376435 // As `dumpsys package` prints a coarse-only grant on Android 14.
377436 const dumpsys = [