Skip to content

Commit 69ed12f

Browse files
committed
[lldb] Add utility to create Mach-O corefile from YAML desc
I've wanted a utility to create a corefile for test purposes given a bit of memory and regsters, for a while. I've written a few API tests over the years that needed exactly this capability -- we have several one-off Mach-O corefile creator utility in the API testsuite to do this. But it's a lot of boilerplate when you only want to specify some register contents and memory contents, to create an API test. This adds yaml2mach-core, a tool that should build on any system, takes a yaml description of register values for one or more threads, optionally memory values for one or more memory regions, and can take a list of UUIDs that will be added as LC_NOTE "load binary" metadata to the corefile so binaries can be loaded into virtual address space in a test scenario. The format of the yaml file looks like cpu: armv7m endian: little threads: - regsets: - flavor: gpr registers: [{name: sp, value: 0x2000fe70}, {name: r7, value: 0x2000fe80}, {name: pc, value: 0x0020392c}, {name: lr, value: 0x0020392d}] memory-regions: - addr: 0x2000fe70 UInt32: [ 0x0000002a, 0x20010e58, 0x00203923, 0x00000001, 0x2000fe88, 0x00203911, 0x2000ffdc, 0xfffffff9 ] - addr: 0x203910 UInt8: [ 0xf8, 0xb5, 0x04, 0xaf, 0x06, 0x4c, 0x07, 0x49, 0x74, 0xf0, 0x2e, 0xf8, 0x01, 0xac, 0x74, 0xf0 ] and that's all that is needed to specify a corefile where four register values are specified (the others will be set to 0), and two memory regions will be emitted. The memory can be specified as an array of UInt8, UInt32, or UInt64, I anticipate that some of these corefiles may have stack values constructed manually and it may be simpler for a human to write them in a particular grouping of values. Accepting "endian" is probably a boondoggle that won't ever come to any use, and honestly I don't 100% know what the correct byte layout would be for a big endian Mach-O file any more. In a RISC-V discussion a month ago, it was noted that register byte layout will be little endian even when there is a big endian defined format for RV, so memory would be byteswapped but registers would not. It may have been better not to pretend to support this, but on the other hand it might be neat to be able to generate a big endian test case simply. I needed this utility for an upcoming patch for ARM Cortex-M processors, to create a test for the change. I took the opportunity to remove two of the "trivial mach-o corefile" creator utilities I've written in the past, which also restricted the tests to only run on Darwin systems because I was using the system headers for Mach-O constant values. rdar://110663219
1 parent 3720d8b commit 69ed12f

28 files changed

+1099
-411
lines changed

lldb/packages/Python/lldbsuite/test/configuration.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@
6464
# Path to the yaml2obj tool. Not optional.
6565
yaml2obj = None
6666

67+
# Path to the yaml2macho-core tool. Not optional.
68+
yaml2macho_core = None
69+
6770
# The arch might dictate some specific CFLAGS to be passed to the toolchain to build
6871
# the inferior programs. The global variable cflags_extras provides a hook to do
6972
# just that.
@@ -174,3 +177,10 @@ def get_yaml2obj_path():
174177
"""
175178
if yaml2obj and os.path.lexists(yaml2obj):
176179
return yaml2obj
180+
181+
def get_yaml2macho_core_path():
182+
"""
183+
Get the path to the yaml2macho-core tool.
184+
"""
185+
if yaml2macho_core and os.path.lexists(yaml2macho_core):
186+
return yaml2macho_core

lldb/packages/Python/lldbsuite/test/dotest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ def parseOptionsAndInitTestdirs():
280280
configuration.llvm_tools_dir = args.llvm_tools_dir
281281
configuration.filecheck = shutil.which("FileCheck", path=args.llvm_tools_dir)
282282
configuration.yaml2obj = shutil.which("yaml2obj", path=args.llvm_tools_dir)
283+
configuration.yaml2macho_core = shutil.which("yaml2macho-core", path=args.llvm_tools_dir)
283284

284285
if not configuration.get_filecheck_path():
285286
logging.warning("No valid FileCheck executable; some tests may fail...")

lldb/packages/Python/lldbsuite/test/lldbtest.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1702,6 +1702,21 @@ def yaml2obj(self, yaml_path, obj_path, max_size=None):
17021702
command += ["--max-size=%d" % max_size]
17031703
self.runBuildCommand(command)
17041704

1705+
def yaml2macho_core(self, yaml_path, obj_path, uuids=None):
1706+
"""
1707+
Create a Mach-O corefile at the given path from a yaml file.
1708+
1709+
Throws subprocess.CalledProcessError if the object could not be created.
1710+
"""
1711+
yaml2macho_core_bin = configuration.get_yaml2macho_core_path()
1712+
if not yaml2macho_core_bin:
1713+
self.assertTrue(False, "No valid yaml2macho-core executable specified")
1714+
if uuids != None:
1715+
command = [yaml2macho_core_bin, "-i", yaml_path, "-o", obj_path, "-u", uuids]
1716+
else:
1717+
command = [yaml2macho_core_bin, "-i", yaml_path, "-o", obj_path]
1718+
self.runBuildCommand(command)
1719+
17051720
def cleanup(self, dictionary=None):
17061721
"""Platform specific way to do cleanup after build."""
17071722
module = builder_module()

lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "lldb/Core/PluginManager.h"
1313
#include "lldb/Core/Section.h"
1414
#include "lldb/Symbol/Symbol.h"
15+
#include "lldb/Target/Target.h"
1516
#include "lldb/Utility/LLDBLog.h"
1617
#include "lldb/Utility/Log.h"
1718
#include "llvm/ADT/DenseSet.h"
@@ -233,6 +234,41 @@ void ObjectFileJSON::CreateSections(SectionList &unified_section_list) {
233234
}
234235
}
235236

237+
bool ObjectFileJSON::SetLoadAddress(Target &target, lldb::addr_t value,
238+
bool value_is_offset) {
239+
Log *log(GetLog(LLDBLog::DynamicLoader));
240+
if (!m_sections_up)
241+
return true;
242+
243+
const bool warn_multiple = true;
244+
245+
addr_t slide = value;
246+
if (!value_is_offset) {
247+
addr_t lowest_addr = LLDB_INVALID_ADDRESS;
248+
for (const SectionSP &section_sp : *m_sections_up) {
249+
addr_t section_load_addr = section_sp->GetFileAddress();
250+
lowest_addr = std::min(lowest_addr, section_load_addr);
251+
}
252+
if (lowest_addr == LLDB_INVALID_ADDRESS)
253+
return false;
254+
slide = value - lowest_addr;
255+
}
256+
257+
// Apply slide to each section's file address.
258+
for (const SectionSP &section_sp : *m_sections_up) {
259+
addr_t section_load_addr = section_sp->GetFileAddress();
260+
if (section_load_addr != LLDB_INVALID_ADDRESS) {
261+
LLDB_LOGF(
262+
log,
263+
"ObjectFileJSON::SetLoadAddress section %s to load addr 0x%" PRIx64,
264+
section_sp->GetName().AsCString(), section_load_addr + slide);
265+
target.SetSectionLoadAddress(section_sp, section_load_addr + slide,
266+
warn_multiple);
267+
}
268+
}
269+
return true;
270+
}
271+
236272
bool ObjectFileJSON::MagicBytesMatch(DataBufferSP data_sp,
237273
lldb::addr_t data_offset,
238274
lldb::addr_t data_length) {

lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ class ObjectFileJSON : public ObjectFile {
8686

8787
Strata CalculateStrata() override { return eStrataUser; }
8888

89+
bool SetLoadAddress(Target &target, lldb::addr_t value,
90+
bool value_is_offset) override;
91+
8992
static bool MagicBytesMatch(lldb::DataBufferSP data_sp, lldb::addr_t offset,
9093
lldb::addr_t length);
9194

lldb/test/API/macosx/arm-corefile-regctx/Makefile

Lines changed: 0 additions & 6 deletions
This file was deleted.

lldb/test/API/macosx/arm-corefile-regctx/TestArmMachoCorefileRegctx.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,14 @@
1313
class TestArmMachoCorefileRegctx(TestBase):
1414
NO_DEBUG_INFO_TESTCASE = True
1515

16-
@skipUnlessDarwin
17-
def setUp(self):
18-
TestBase.setUp(self)
19-
self.build()
20-
self.create_corefile = self.getBuildArtifact("a.out")
21-
self.corefile = self.getBuildArtifact("core")
22-
2316
def test_armv7_corefile(self):
2417
### Create corefile
25-
retcode = call(self.create_corefile + " armv7 " + self.corefile, shell=True)
18+
corefile = self.getBuildArtifact("core")
19+
self.yaml2macho_core("armv7m.yaml", corefile)
2620

2721
target = self.dbg.CreateTarget("")
2822
err = lldb.SBError()
29-
process = target.LoadCore(self.corefile)
23+
process = target.LoadCore(corefile)
3024
self.assertTrue(process.IsValid())
3125
thread = process.GetSelectedThread()
3226
frame = thread.GetSelectedFrame()
@@ -50,11 +44,12 @@ def test_armv7_corefile(self):
5044

5145
def test_arm64_corefile(self):
5246
### Create corefile
53-
retcode = call(self.create_corefile + " arm64 " + self.corefile, shell=True)
47+
corefile = self.getBuildArtifact("core")
48+
self.yaml2macho_core("arm64.yaml", corefile)
5449

5550
target = self.dbg.CreateTarget("")
5651
err = lldb.SBError()
57-
process = target.LoadCore(self.corefile)
52+
process = target.LoadCore(corefile)
5853
self.assertTrue(process.IsValid())
5954
thread = process.GetSelectedThread()
6055
frame = thread.GetSelectedFrame()
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
cpu: arm64
2+
endian: little
3+
threads:
4+
# (lldb) reg read
5+
# % pbpaste | grep = | sed 's, ,,g' | awk -F= '{print "{name: " $1 ", value: " $2 "},"}'
6+
- regsets:
7+
- flavor: gpr
8+
registers: [
9+
{name: x0, value: 0x0000000000000001}, {name: x1, value: 0x000000016fdff3c0},
10+
{name: x2, value: 0x000000016fdff3d0}, {name: x3, value: 0x000000016fdff510},
11+
{name: x4, value: 0x0000000000000000}, {name: x5, value: 0x0000000000000000},
12+
{name: x6, value: 0x0000000000000000}, {name: x7, value: 0x0000000000000000},
13+
{name: x8, value: 0x000000010000d910}, {name: x9, value: 0x0000000000000001},
14+
{name: x10, value: 0xe1e88de000000000}, {name: x11, value: 0x0000000000000003},
15+
{name: x12, value: 0x0000000000000148}, {name: x13, value: 0x0000000000004000},
16+
{name: x14, value: 0x0000000000000008}, {name: x15, value: 0x0000000000000000},
17+
{name: x16, value: 0x0000000000000000}, {name: x17, value: 0x0000000100003f5c},
18+
{name: x18, value: 0x0000000000000000}, {name: x19, value: 0x0000000100003f5c},
19+
{name: x20, value: 0x000000010000c000}, {name: x21, value: 0x000000010000d910},
20+
{name: x22, value: 0x000000016fdff250}, {name: x23, value: 0x000000018ce12366},
21+
{name: x24, value: 0x000000016fdff1d0}, {name: x25, value: 0x0000000000000001},
22+
{name: x26, value: 0x0000000000000000}, {name: x27, value: 0x0000000000000000},
23+
{name: x28, value: 0x0000000000000000}, {name: fp, value: 0x000000016fdff3a0},
24+
{name: lr, value: 0x000000018cd97f28}, {name: sp, value: 0x000000016fdff140},
25+
{name: pc, value: 0x0000000100003f5c}, {name: cpsr, value: 0x80001000}
26+
]
27+
- flavor: exc
28+
registers: [ {name: far, value: 0x0000000100003f5c},
29+
{name: esr, value: 0xf2000000},
30+
{name: exception, value: 0x00000000}
31+
]
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
cpu: armv7m
2+
endian: little
3+
threads:
4+
# (lldb) reg read
5+
# % pbpaste | grep = | sed 's, ,,g' | awk -F= '{print "{name: " $1 ", value: " $2 "},"}'
6+
- regsets:
7+
- flavor: gpr
8+
registers: [
9+
{name: r0, value: 0x00010000}, {name: r1, value: 0x00020000},
10+
{name: r2, value: 0x00030000}, {name: r3, value: 0x00040000},
11+
{name: r4, value: 0x00050000}, {name: r5, value: 0x00060000},
12+
{name: r6, value: 0x00070000}, {name: r7, value: 0x00080000},
13+
{name: r8, value: 0x00090000}, {name: r9, value: 0x000a0000},
14+
{name: r10, value: 0x000b0000}, {name: r11, value: 0x000c0000},
15+
{name: r12, value: 0x000d0000}, {name: sp, value: 0x000e0000},
16+
{name: lr, value: 0x000f0000}, {name: pc, value: 0x00100000},
17+
{name: cpsr, value: 0x00110000}
18+
]
19+
- flavor: exc
20+
registers: [ {name: far, value: 0x00003f5c},
21+
{name: esr, value: 0xf2000000},
22+
{name: exception, value: 0x00000000}
23+
]
24+
25+
memory-regions:
26+
# $sp is 0x000e0000, have bytes surrounding that address
27+
- addr: 0x000dffe0
28+
UInt8: [
29+
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
30+
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
31+
0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
32+
0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
33+
0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c,
34+
0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
35+
0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
36+
0x3f
37+
]

0 commit comments

Comments
 (0)