Files
DroidDeck/tools/tests/test_faultreport.py
MaxsTechReview 0e225d0c6e FEX: preload the fault report only where x86 loaders look (#551)
Preload the FEX fault report only where x86 loaders look

droiddeck-fex put the x86 libfaultreport.so in LD_PRELOAD, which FEX's own
arm64 loader reads too - and FEXServer's, and every child FEX's - so each
x86 start printed "ERROR: ld.so: object '.../libfaultreport.so' from
LD_PRELOAD cannot be preloaded", and an i386 child added "wrong ELF class".
It is now listed in the x86 runtime's own /etc/ld.so.preload, which only
the loaders inside FEX read, as /usr/$LIB/droiddeck/libfaultreport.so.

faultreport.c builds for i386 too, so 32-bit programs get the report as
well instead of an error.
2026-10-10 19:12:51 -04:00

45 lines
1.9 KiB
Python

import os
from pathlib import Path
import shutil
import subprocess
import tempfile
import unittest
SOURCE = Path(__file__).resolve().parents[1] / "linuxfs/fex/faultreport.c"
PROGRAM = 'int main(void) { __asm__ volatile("ud2"); return 0; }\n'
FLAGS = ["-shared", "-fPIC", "-O2", "-Wall", "-Wextra", "-Werror", "-nostdlib", "-fno-stack-protector", "-fno-builtin",
"-fvisibility=hidden"]
@unittest.skipUnless(shutil.which("gcc") and os.uname().machine == "x86_64", "needs gcc on an x86-64 host")
class FaultReportTest(unittest.TestCase):
def setUp(self):
self.tmp = Path(tempfile.mkdtemp())
self.addCleanup(shutil.rmtree, self.tmp, ignore_errors=True)
(self.tmp / "program.c").write_text(PROGRAM)
def build(self, bits):
library, program = self.tmp / ("libfaultreport%s.so" % bits), self.tmp / ("program%s" % bits)
if subprocess.run(["gcc", "-m" + bits, "-o", str(program), str(self.tmp / "program.c")], capture_output=True).returncode:
self.skipTest("gcc cannot build %s-bit programs here" % bits)
made = subprocess.run(["gcc", "-m" + bits] + FLAGS + ["-o", str(library), str(SOURCE)], capture_output=True, text=True)
self.assertEqual(made.returncode, 0, made.stderr)
return library, program
def check(self, bits):
library, program = self.build(bits)
result = subprocess.run([str(program)], env=dict(os.environ, LD_PRELOAD=str(library)), capture_output=True, text=True)
self.assertEqual(result.returncode, -4)
self.assertRegex(result.stderr, r"droiddeck-fex: SIGILL at 0x[0-9a-f]+ \(si_code 02\), offset 0x[0-9a-f]+, bytes 0f 0b ")
self.assertIn("program%s" % bits, result.stderr)
def test_an_x86_64_program_reports_its_illegal_instruction(self):
self.check("64")
def test_an_i386_program_reports_its_illegal_instruction(self):
self.check("32")
if __name__ == "__main__":
unittest.main()