xref: /llvm-project/lldb/test/API/macosx/lc-note/firmware-corefile/TestFirmwareCorefiles.py (revision 1eeeab82c6eb185f5139e633a59c2dbcb15616e4)
1"""Test that corefiles with LC_NOTE "kern ver str" and "main bin spec" load commands works."""
2
3
4import os
5import re
6import subprocess
7
8import lldb
9from lldbsuite.test.decorators import *
10from lldbsuite.test.lldbtest import *
11from lldbsuite.test import lldbutil
12
13
14class TestFirmwareCorefiles(TestBase):
15    @skipIf(
16        debug_info=no_match(["dsym"]),
17        bugnumber="This test is looking explicitly for a dSYM",
18    )
19    @skipIf(archs=no_match(["x86_64", "arm64", "arm64e", "aarch64"]))
20    @skipIfRemote
21    @skipUnlessDarwin
22    def test_lc_note_version_string(self):
23        self.build()
24        aout_exe_basename = "a.out"
25        aout_exe = self.getBuildArtifact(aout_exe_basename)
26        verstr_corefile = self.getBuildArtifact("verstr.core")
27        verstr_corefile_invalid_ident = self.getBuildArtifact(
28            "verstr-invalid-ident.core"
29        )
30        verstr_corefile_addr = self.getBuildArtifact("verstr-addr.core")
31        create_corefile = self.getBuildArtifact("create-empty-corefile")
32        slide = 0x70000000000
33        call(
34            create_corefile
35            + " version-string "
36            + verstr_corefile
37            + " "
38            + aout_exe
39            + " 0xffffffffffffffff 0xffffffffffffffff",
40            shell=True,
41        )
42        call(
43            create_corefile
44            + " version-string "
45            + verstr_corefile_invalid_ident
46            + ' "" '
47            + "0xffffffffffffffff 0xffffffffffffffff",
48            shell=True,
49        )
50        call(
51            create_corefile
52            + " version-string "
53            + verstr_corefile_addr
54            + " "
55            + aout_exe
56            + (" 0x%x" % slide)
57            + " 0xffffffffffffffff",
58            shell=True,
59        )
60
61        if self.TraceOn():
62            self.runCmd("log enable lldb dyld host")
63            self.addTearDownHook(lambda: self.runCmd("log disable lldb dyld host"))
64
65        # Register the a.out binary with this UUID in lldb's global module
66        # cache, then throw the Target away.
67        target = self.dbg.CreateTarget(aout_exe)
68        self.dbg.DeleteTarget(target)
69
70        # First, try the "kern ver str" corefile
71        target = self.dbg.CreateTarget("")
72        err = lldb.SBError()
73        if self.TraceOn():
74            self.runCmd("script print('loading corefile %s')" % verstr_corefile)
75        process = target.LoadCore(verstr_corefile)
76        self.assertTrue(process.IsValid())
77        if self.TraceOn():
78            self.runCmd("image list")
79            self.runCmd("target mod dump sections")
80        self.assertEqual(target.GetNumModules(), 1)
81        fspec = target.GetModuleAtIndex(0).GetFileSpec()
82        self.assertEqual(fspec.GetFilename(), aout_exe_basename)
83        self.dbg.DeleteTarget(target)
84
85        # Second, try the "kern ver str" corefile which has an invalid ident,
86        # make sure we don't crash.
87        target = self.dbg.CreateTarget("")
88        err = lldb.SBError()
89        if self.TraceOn():
90            self.runCmd(
91                "script print('loading corefile %s')" % verstr_corefile_invalid_ident
92            )
93        process = target.LoadCore(verstr_corefile_invalid_ident)
94        self.assertTrue(process.IsValid())
95
96        # Third, try the "kern ver str" corefile where it loads at an address
97        target = self.dbg.CreateTarget("")
98        err = lldb.SBError()
99        if self.TraceOn():
100            self.runCmd("script print('loading corefile %s')" % verstr_corefile_addr)
101        process = target.LoadCore(verstr_corefile_addr)
102        self.assertTrue(process.IsValid())
103        if self.TraceOn():
104            self.runCmd("image list")
105            self.runCmd("target mod dump sections")
106        self.assertEqual(target.GetNumModules(), 1)
107        fspec = target.GetModuleAtIndex(0).GetFileSpec()
108        self.assertEqual(fspec.GetFilename(), aout_exe_basename)
109        main_sym = target.GetModuleAtIndex(0).FindSymbol("main", lldb.eSymbolTypeAny)
110        main_addr = main_sym.GetStartAddress()
111        self.assertGreater(main_addr.GetLoadAddress(target), slide)
112        self.assertNotEqual(main_addr.GetLoadAddress(target), lldb.LLDB_INVALID_ADDRESS)
113        self.dbg.DeleteTarget(target)
114
115    @skipIf(
116        debug_info=no_match(["dsym"]),
117        bugnumber="This test is looking explicitly for a dSYM",
118    )
119    @skipIf(archs=no_match(["x86_64", "arm64", "arm64e", "aarch64"]))
120    @skipIfRemote
121    @skipUnlessDarwin
122    def test_lc_note_main_bin_spec(self):
123        self.build()
124        aout_exe_basename = "a.out"
125        aout_exe = self.getBuildArtifact(aout_exe_basename)
126        create_corefile = self.getBuildArtifact("create-empty-corefile")
127        binspec_corefile = self.getBuildArtifact("binspec.core")
128        binspec_corefile_addr = self.getBuildArtifact("binspec-addr.core")
129        binspec_corefile_slideonly = self.getBuildArtifact(
130            "binspec-addr-slideonly.core"
131        )
132
133        slide = 0x70000000000
134
135        ### Create our corefile
136        # 0xffffffffffffffff means load address unknown
137        call(
138            create_corefile
139            + " main-bin-spec "
140            + binspec_corefile
141            + " "
142            + aout_exe
143            + " 0xffffffffffffffff 0xffffffffffffffff",
144            shell=True,
145        )
146        call(
147            create_corefile
148            + " main-bin-spec "
149            + binspec_corefile_addr
150            + " "
151            + aout_exe
152            + (" 0x%x" % slide)
153            + " 0xffffffffffffffff",
154            shell=True,
155        )
156        call(
157            create_corefile
158            + " main-bin-spec "
159            + binspec_corefile_slideonly
160            + " "
161            + aout_exe
162            + " 0xffffffffffffffff"
163            + (" 0x%x" % slide),
164            shell=True,
165        )
166
167        if self.TraceOn():
168            self.runCmd("log enable lldb dyld host")
169            self.addTearDownHook(lambda: self.runCmd("log disable lldb dyld host"))
170
171        # Register the a.out binary with this UUID in lldb's global module
172        # cache, then throw the Target away.
173        target = self.dbg.CreateTarget(aout_exe)
174        self.dbg.DeleteTarget(target)
175
176        # First, try the "main bin spec" corefile
177        target = self.dbg.CreateTarget("")
178        if self.TraceOn():
179            self.runCmd("script print('loading corefile %s')" % binspec_corefile)
180        process = target.LoadCore(binspec_corefile)
181        self.assertTrue(process.IsValid())
182        if self.TraceOn():
183            self.runCmd("image list")
184            self.runCmd("target mod dump sections")
185        self.assertEqual(target.GetNumModules(), 1)
186        fspec = target.GetModuleAtIndex(0).GetFileSpec()
187        self.assertEqual(fspec.GetFilename(), aout_exe_basename)
188        self.dbg.DeleteTarget(target)
189
190        # Second, try the "main bin spec" corefile where it loads at an address
191        target = self.dbg.CreateTarget("")
192        if self.TraceOn():
193            self.runCmd("script print('loading corefile %s')" % binspec_corefile_addr)
194        process = target.LoadCore(binspec_corefile_addr)
195        self.assertTrue(process.IsValid())
196        if self.TraceOn():
197            self.runCmd("image list")
198            self.runCmd("target mod dump sections")
199        self.assertEqual(target.GetNumModules(), 1)
200        fspec = target.GetModuleAtIndex(0).GetFileSpec()
201        self.assertEqual(fspec.GetFilename(), aout_exe_basename)
202        main_sym = target.GetModuleAtIndex(0).FindSymbol("main", lldb.eSymbolTypeAny)
203        main_addr = main_sym.GetStartAddress()
204        self.assertGreater(main_addr.GetLoadAddress(target), slide)
205        self.assertNotEqual(main_addr.GetLoadAddress(target), lldb.LLDB_INVALID_ADDRESS)
206        self.dbg.DeleteTarget(target)
207
208        # Third, try the "main bin spec" corefile where it loads at a slide
209        target = self.dbg.CreateTarget("")
210        if self.TraceOn():
211            self.runCmd(
212                "script print('loading corefile %s')" % binspec_corefile_slideonly
213            )
214        process = target.LoadCore(binspec_corefile_slideonly)
215        self.assertTrue(process.IsValid())
216        if self.TraceOn():
217            self.runCmd("image list")
218            self.runCmd("target mod dump sections")
219        self.assertEqual(target.GetNumModules(), 1)
220        fspec = target.GetModuleAtIndex(0).GetFileSpec()
221        self.assertEqual(fspec.GetFilename(), aout_exe_basename)
222        main_sym = target.GetModuleAtIndex(0).FindSymbol("main", lldb.eSymbolTypeAny)
223        main_addr = main_sym.GetStartAddress()
224        self.assertGreater(main_addr.GetLoadAddress(target), slide)
225        self.assertNotEqual(main_addr.GetLoadAddress(target), lldb.LLDB_INVALID_ADDRESS)
226        self.dbg.DeleteTarget(target)
227
228    @skipIf(
229        debug_info=no_match(["dsym"]),
230        bugnumber="This test is looking explicitly for a dSYM",
231    )
232    @skipIf(archs=no_match(["x86_64", "arm64", "arm64e", "aarch64"]))
233    @skipIfRemote
234    @skipUnlessDarwin
235    def test_lc_note_main_bin_spec_os_plugin(self):
236        self.build()
237        aout_exe = self.getBuildArtifact("a.out")
238        aout_exe_basename = "a.out"
239        create_corefile = self.getBuildArtifact("create-empty-corefile")
240        binspec_corefile_addr = self.getBuildArtifact("binspec-addr.core")
241
242        slide = 0x70000000000
243
244        ### Create our corefile
245        # 0xffffffffffffffff means load address unknown
246        call(
247            create_corefile
248            + " main-bin-spec "
249            + binspec_corefile_addr
250            + " "
251            + aout_exe
252            + (" 0x%x" % slide)
253            + " 0xffffffffffffffff",
254            shell=True,
255        )
256
257        ## We can hook in our dsym-for-uuid shell script to lldb with this env
258        ## var instead of requiring a defaults write.
259        dsym_for_uuid = self.getBuildArtifact("dsym-for-uuid.sh")
260        os.environ["LLDB_APPLE_DSYMFORUUID_EXECUTABLE"] = dsym_for_uuid
261        if self.TraceOn():
262            print("Setting env var LLDB_APPLE_DSYMFORUUID_EXECUTABLE=" + dsym_for_uuid)
263        self.addTearDownHook(
264            lambda: os.environ.pop("LLDB_APPLE_DSYMFORUUID_EXECUTABLE", None)
265        )
266
267        self.runCmd("settings set target.load-script-from-symbol-file true")
268        self.addTearDownHook(
269            lambda: self.runCmd(
270                "settings set target.load-script-from-symbol-file false"
271            )
272        )
273
274        dsym_python_dir = os.path.join(
275            "%s.dSYM" % aout_exe, "Contents", "Resources", "Python"
276        )
277        os.makedirs(dsym_python_dir)
278        python_os_plugin_path = os.path.join(self.getSourceDir(), "operating_system.py")
279        python_init = [
280            "def __lldb_init_module(debugger, internal_dict):",
281            "  debugger.HandleCommand('settings set target.process.python-os-plugin-path %s')"
282            % python_os_plugin_path,
283        ]
284        with open(os.path.join(dsym_python_dir, "a_out.py"), "w") as writer:
285            for l in python_init:
286                writer.write(l + "\n")
287
288        dwarfdump_uuid_regex = re.compile("UUID: ([-0-9a-fA-F]+) \(([^\(]+)\) .*")
289        dwarfdump_cmd_output = subprocess.check_output(
290            ('/usr/bin/dwarfdump --uuid "%s"' % aout_exe), shell=True
291        ).decode("utf-8")
292        aout_uuid = None
293        for line in dwarfdump_cmd_output.splitlines():
294            match = dwarfdump_uuid_regex.search(line)
295            if match:
296                aout_uuid = match.group(1)
297        self.assertNotEqual(aout_uuid, None, "Could not get uuid of built a.out")
298
299        ###  Create our dsym-for-uuid shell script which returns aout_exe
300        shell_cmds = [
301            "#! /bin/sh",
302            "# the last argument is the uuid",
303            "while [ $# -gt 1 ]",
304            "do",
305            "  shift",
306            "done",
307            "ret=0",
308            'echo "<?xml version=\\"1.0\\" encoding=\\"UTF-8\\"?>"',
309            'echo "<!DOCTYPE plist PUBLIC \\"-//Apple//DTD PLIST 1.0//EN\\" \\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\\">"',
310            'echo "<plist version=\\"1.0\\">"',
311            "",
312            'if [ "$1" != "%s" ]' % (aout_uuid),
313            "then",
314            '  echo "<key>DBGError</key><string>not found</string>"',
315            '  echo "</plist>"',
316            "  exit 1",
317            "fi",
318            "  uuid=%s" % aout_uuid,
319            "  bin=%s" % aout_exe,
320            "  dsym=%s.dSYM/Contents/Resources/DWARF/%s"
321            % (aout_exe, os.path.basename(aout_exe)),
322            'echo "<dict><key>$uuid</key><dict>"',
323            "",
324            'echo "<key>DBGDSYMPath</key><string>$dsym</string>"',
325            'echo "<key>DBGSymbolRichExecutable</key><string>$bin</string>"',
326            'echo "</dict></dict></plist>"',
327            "exit $ret",
328        ]
329
330        with open(dsym_for_uuid, "w") as writer:
331            for l in shell_cmds:
332                writer.write(l + "\n")
333
334        os.chmod(dsym_for_uuid, 0o755)
335
336        ### Now run lldb on the corefile
337        ### which will give us a UUID
338        ### which we call dsym-for-uuid.sh with
339        ### which gives us a binary and dSYM
340        ### which lldb should load!
341
342        if self.TraceOn():
343            self.runCmd("log enable lldb dyld host")
344            self.addTearDownHook(lambda: self.runCmd("log disable lldb dyld host"))
345        # Now load the binary and confirm that we load the OS plugin.
346        target = self.dbg.CreateTarget("")
347
348        if self.TraceOn():
349            self.runCmd(
350                "script print('loading corefile %s with OS plugin')"
351                % binspec_corefile_addr
352            )
353
354        process = target.LoadCore(binspec_corefile_addr)
355        self.assertTrue(process.IsValid())
356        if self.TraceOn():
357            self.runCmd("image list")
358            self.runCmd("target mod dump sections")
359            self.runCmd("thread list")
360        self.assertEqual(target.GetNumModules(), 1)
361        fspec = target.GetModuleAtIndex(0).GetFileSpec()
362        self.assertEqual(fspec.GetFilename(), aout_exe_basename)
363
364        # Verify our OS plug-in threads showed up
365        thread = process.GetThreadByID(0x111111111)
366        self.assertTrue(
367            thread.IsValid(),
368            "Make sure there is a thread 0x111111111 after we load the python OS plug-in",
369        )
370        thread = process.GetThreadByID(0x222222222)
371        self.assertTrue(
372            thread.IsValid(),
373            "Make sure there is a thread 0x222222222 after we load the python OS plug-in",
374        )
375        thread = process.GetThreadByID(0x333333333)
376        self.assertTrue(
377            thread.IsValid(),
378            "Make sure there is a thread 0x333333333 after we load the python OS plug-in",
379        )
380
381        self.runCmd("settings clear target.process.python-os-plugin-path")
382        self.dbg.DeleteTarget(target)
383