also looking at this
perf(android): ship libcraft.so without its DWARF, and keep the DWARF for symbols
#217
6 files
+235
-14
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | import { describe, expect, it } from 'bun:test' |
| 2 | 2 | import { readFileSync } from 'node:fs' |
| 3 | 3 | import { join } from 'node:path' |
| 4 | import { 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' | |
| 4 | import { 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' | |
| 5 | 5 | |
| 6 | 6 | const ESC = String.fromCharCode(27) |
| 7 | 7 | |
| @@ -372,6 +372,65 @@ describe('cold-start deep links', () => { | ||
| 372 | 372 | }) |
| 373 | 373 | }) |
| 374 | 374 | |
| 375 | /** A minimal ELF64 little-endian file whose section header table names `sections`. */ | |
| 376 | function 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 | ||
| 403 | describe('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 | ||
| 375 | 434 | describe('runtime permission state', () => { |
| 376 | 435 | // As `dumpsys package` prints a coarse-only grant on Android 14. |
| 377 | 436 | const dumpsys = [ |