mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-28 18:46:37 +08:00
Merge remote-tracking branch 'origin/GP-6456_ghidra_red_ImproveTenetLoader--SQUASHED'
This commit is contained in:
+33
-10
@@ -173,7 +173,29 @@ public class TenetLoader implements Loader {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
try {
|
||||||
|
final byte[] bytes = new byte[1000];
|
||||||
|
provider.getInputStream(0).read(bytes);
|
||||||
|
final String[] lines = new String(bytes, StandardCharsets.UTF_8).split("\n");
|
||||||
|
|
||||||
|
if (lines.length == 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip last line as it could be a partial one
|
||||||
|
for (int i = 0; i < (lines.length - 1); i++) {
|
||||||
|
final String line = lines[i];
|
||||||
|
if (((i != 0) && SLIDE_PATTERN.matcher(line).find()) ||
|
||||||
|
(!SLIDE_PATTERN.matcher(line).find() && !REG_PATTERN.matcher(line).find())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (final IOException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -209,7 +231,8 @@ public class TenetLoader implements Loader {
|
|||||||
final DomainObject domainObject, final boolean loadIntoProgram,
|
final DomainObject domainObject, final boolean loadIntoProgram,
|
||||||
final boolean mirrorFsLayout) {
|
final boolean mirrorFsLayout) {
|
||||||
final List<Option> list = new ArrayList<>();
|
final List<Option> list = new ArrayList<>();
|
||||||
list.add(new DomainFileOption(DOMAIN_FILE_OPTION_NAME, "", false));
|
list.add(new DomainFileOption(DOMAIN_FILE_OPTION_NAME,
|
||||||
|
COMMAND_LINE_ARG_PREFIX + "-associatedProgram", false));
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,7 +281,7 @@ public class TenetLoader implements Loader {
|
|||||||
|
|
||||||
final long start = System.currentTimeMillis();
|
final long start = System.currentTimeMillis();
|
||||||
|
|
||||||
trace = this.loadTrace(settings.provider(), settings.importName(), program,
|
trace = loadTrace(settings.provider(), settings.importName(), program,
|
||||||
settings.consumer(), settings.log(), settings.monitor());
|
settings.consumer(), settings.log(), settings.monitor());
|
||||||
|
|
||||||
final long loadDone = System.currentTimeMillis();
|
final long loadDone = System.currentTimeMillis();
|
||||||
@@ -347,7 +370,7 @@ public class TenetLoader implements Loader {
|
|||||||
|
|
||||||
snapNumber++;
|
snapNumber++;
|
||||||
|
|
||||||
this.setupMemoryAndMapping(program, trace, slideValue, snap);
|
setupMemoryAndMapping(program, trace, slideValue, snap);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
while (line != null) {
|
while (line != null) {
|
||||||
@@ -376,8 +399,8 @@ public class TenetLoader implements Loader {
|
|||||||
}
|
}
|
||||||
curIp = Long.parseLong(ipMatcher.group(1), 16);
|
curIp = Long.parseLong(ipMatcher.group(1), 16);
|
||||||
|
|
||||||
if (!this.parseRegisterOperations(snap, curIp, line, lineNumber,
|
if (!parseRegisterOperations(snap, curIp, line, lineNumber, traceThread,
|
||||||
traceThread, trace, log, monitor)) {
|
trace, log, monitor)) {
|
||||||
errorCount++;
|
errorCount++;
|
||||||
lineNumber++;
|
lineNumber++;
|
||||||
monitor.setProgress(lineNumber);
|
monitor.setProgress(lineNumber);
|
||||||
@@ -391,7 +414,7 @@ public class TenetLoader implements Loader {
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
this.parseMemoryOperations(snap, curIp, line, trace, monitor);
|
parseMemoryOperations(snap, curIp, line, trace, monitor);
|
||||||
|
|
||||||
lineNumber++;
|
lineNumber++;
|
||||||
monitor.setProgress(lineNumber);
|
monitor.setProgress(lineNumber);
|
||||||
@@ -475,8 +498,9 @@ public class TenetLoader implements Loader {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupMemoryAndMapping(final Program program, final Trace trace, final long slideValue,
|
private void setupMemoryAndMapping(final Program program, final Trace trace,
|
||||||
final long snap) throws DuplicateNameException, TraceOverlappedRegionException {
|
final long slideValue, final long snap)
|
||||||
|
throws DuplicateNameException, TraceOverlappedRegionException {
|
||||||
final TraceModuleManager modMan = trace.getModuleManager();
|
final TraceModuleManager modMan = trace.getModuleManager();
|
||||||
final TraceStaticMappingManager mapMan = trace.getStaticMappingManager();
|
final TraceStaticMappingManager mapMan = trace.getStaticMappingManager();
|
||||||
final URL projectUrl = program.getDomainFile().getLocalProjectURL("");
|
final URL projectUrl = program.getDomainFile().getLocalProjectURL("");
|
||||||
@@ -522,5 +546,4 @@ public class TenetLoader implements Loader {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+123
-25
@@ -65,7 +65,7 @@ public class TenetLoaderTest extends AbstractGhidraHeadedDebuggerTest {
|
|||||||
|
|
||||||
private Trace createTraceWithLoader(final String testFile) throws Exception {
|
private Trace createTraceWithLoader(final String testFile) throws Exception {
|
||||||
this.createProgram();
|
this.createProgram();
|
||||||
this.intoProject(this.program);
|
intoProject(program);
|
||||||
|
|
||||||
final TenetLoader loader = new TenetLoader();
|
final TenetLoader loader = new TenetLoader();
|
||||||
final ByteProvider provider =
|
final ByteProvider provider =
|
||||||
@@ -76,11 +76,11 @@ public class TenetLoaderTest extends AbstractGhidraHeadedDebuggerTest {
|
|||||||
loader.getDefaultOptions(provider, loadSpec, null, false, false);
|
loader.getDefaultOptions(provider, loadSpec, null, false, false);
|
||||||
final Option programOption = Unique.assertOne(
|
final Option programOption = Unique.assertOne(
|
||||||
options.stream().filter(o -> o.getName().equals(TenetLoader.DOMAIN_FILE_OPTION_NAME)));
|
options.stream().filter(o -> o.getName().equals(TenetLoader.DOMAIN_FILE_OPTION_NAME)));
|
||||||
programOption.setValue(this.program.getDomainFile().getPathname());
|
programOption.setValue(program.getDomainFile().getPathname());
|
||||||
|
|
||||||
final MessageLog log = new MessageLog();
|
final MessageLog log = new MessageLog();
|
||||||
final ImporterSettings settings = new ImporterSettings(provider, "test",
|
final ImporterSettings settings = new ImporterSettings(provider, "test", env.getProject(),
|
||||||
this.env.getProject(), "/", false, loadSpec, options, this, log, this.monitor);
|
"/", false, loadSpec, options, this, log, monitor);
|
||||||
|
|
||||||
final LoadResults<? extends DomainObject> results = loader.load(settings);
|
final LoadResults<? extends DomainObject> results = loader.load(settings);
|
||||||
if (!(results.getPrimary().domainObject instanceof final Trace trace)) {
|
if (!(results.getPrimary().domainObject instanceof final Trace trace)) {
|
||||||
@@ -99,11 +99,54 @@ public class TenetLoaderTest extends AbstractGhidraHeadedDebuggerTest {
|
|||||||
public void testBadRegisterNames() throws Exception {
|
public void testBadRegisterNames() throws Exception {
|
||||||
final String testFile = """
|
final String testFile = """
|
||||||
pc=0x1,rax=0x1234
|
pc=0x1,rax=0x1234
|
||||||
|
pc=0x2,rax=0x1234
|
||||||
|
pc=0x3,rax=0x1234
|
||||||
|
pc=0x4,rax=0x1234
|
||||||
|
pc=0x5,rax=0x1234
|
||||||
|
pc=0x6,rax=0x1234
|
||||||
|
pc=0x7,rax=0x1234
|
||||||
|
pc=0x8,rax=0x1234
|
||||||
|
pc=0x9,rax=0x1234
|
||||||
|
pc=0xa,rax=0x1234
|
||||||
""";
|
""";
|
||||||
final Trace trace = this.createTraceWithLoader(testFile);
|
final Trace trace = createTraceWithLoader(testFile);
|
||||||
assertNotNull(trace);
|
assertNotNull(trace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test the file content check for the loader
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testFileContent() throws Exception {
|
||||||
|
final String junk_line = "asdf\n";
|
||||||
|
final String slide_line = "slide=0x0\n";
|
||||||
|
final String reg_line = "pc=0x1,r0=0x1234\n";
|
||||||
|
|
||||||
|
this.createProgram();
|
||||||
|
intoProject(program);
|
||||||
|
|
||||||
|
final TenetLoader loader = new TenetLoader();
|
||||||
|
|
||||||
|
ByteProvider provider = new ByteArrayProvider("test.tenet", (junk_line).getBytes("utf-8"));
|
||||||
|
assertEquals(0, loader.findSupportedLoadSpecs(provider).size());
|
||||||
|
|
||||||
|
provider = new ByteArrayProvider("test.tenet", (slide_line + junk_line).getBytes("utf-8"));
|
||||||
|
assertEquals(0, loader.findSupportedLoadSpecs(provider).size());
|
||||||
|
|
||||||
|
provider = new ByteArrayProvider("test.tenet", (reg_line + junk_line).getBytes("utf-8"));
|
||||||
|
assertEquals(0, loader.findSupportedLoadSpecs(provider).size());
|
||||||
|
|
||||||
|
provider = new ByteArrayProvider("test.tenet",
|
||||||
|
(reg_line + slide_line + reg_line.repeat(TenetLoader.ERROR_THRESHOLD))
|
||||||
|
.getBytes("utf-8"));
|
||||||
|
assertEquals(0, loader.findSupportedLoadSpecs(provider).size());
|
||||||
|
|
||||||
|
provider = new ByteArrayProvider("test.tenet",
|
||||||
|
(slide_line + reg_line.repeat(TenetLoader.ERROR_THRESHOLD - 2) + junk_line)
|
||||||
|
.getBytes("utf-8"));
|
||||||
|
assertEquals(0, loader.findSupportedLoadSpecs(provider).size());
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Test the filename check for the loader
|
* Test the filename check for the loader
|
||||||
*/
|
*/
|
||||||
@@ -113,10 +156,18 @@ public class TenetLoaderTest extends AbstractGhidraHeadedDebuggerTest {
|
|||||||
slide=0x0
|
slide=0x0
|
||||||
pc=0x1,r0=0x1234
|
pc=0x1,r0=0x1234
|
||||||
pc=0x2,r1=0x4321
|
pc=0x2,r1=0x4321
|
||||||
|
pc=0x3,r1=0x4321
|
||||||
|
pc=0x4,r1=0x4321
|
||||||
|
pc=0x5,r1=0x4321
|
||||||
|
pc=0x6,r1=0x4321
|
||||||
|
pc=0x7,r1=0x4321
|
||||||
|
pc=0x8,r1=0x4321
|
||||||
|
pc=0x9,r1=0x4321
|
||||||
|
pc=0xa,r1=0x4321
|
||||||
""";
|
""";
|
||||||
|
|
||||||
this.createProgram();
|
this.createProgram();
|
||||||
this.intoProject(this.program);
|
intoProject(program);
|
||||||
|
|
||||||
final TenetLoader loader = new TenetLoader();
|
final TenetLoader loader = new TenetLoader();
|
||||||
|
|
||||||
@@ -139,18 +190,26 @@ public class TenetLoaderTest extends AbstractGhidraHeadedDebuggerTest {
|
|||||||
slide=0x0
|
slide=0x0
|
||||||
pc=0x1,r0=0x1234
|
pc=0x1,r0=0x1234
|
||||||
pc=0x2,r1=0x4321
|
pc=0x2,r1=0x4321
|
||||||
|
pc=0x3,r1=0x4321
|
||||||
|
pc=0x4,r1=0x4321
|
||||||
|
pc=0x5,r1=0x4321
|
||||||
|
pc=0x6,r1=0x4321
|
||||||
|
pc=0x7,r1=0x4321
|
||||||
|
pc=0x8,r1=0x4321
|
||||||
|
pc=0x9,r1=0x4321
|
||||||
|
pc=0xa,r1=0x4321
|
||||||
""";
|
""";
|
||||||
|
|
||||||
addPlugin(this.tool, DebuggerRegistersPlugin.class);
|
addPlugin(tool, DebuggerRegistersPlugin.class);
|
||||||
addPlugin(this.tool, DebuggerModelPlugin.class);
|
addPlugin(tool, DebuggerModelPlugin.class);
|
||||||
addPlugin(this.tool, DebuggerThreadsPlugin.class);
|
addPlugin(tool, DebuggerThreadsPlugin.class);
|
||||||
addPlugin(this.tool, DebuggerListingPlugin.class);
|
addPlugin(tool, DebuggerListingPlugin.class);
|
||||||
addPlugin(this.tool, DebuggerTimePlugin.class);
|
addPlugin(tool, DebuggerTimePlugin.class);
|
||||||
|
|
||||||
final Trace trace = this.createTraceWithLoader(testFile);
|
final Trace trace = createTraceWithLoader(testFile);
|
||||||
|
|
||||||
this.traceManager.openTrace(trace);
|
traceManager.openTrace(trace);
|
||||||
this.traceManager.activateTrace(trace);
|
traceManager.activateTrace(trace);
|
||||||
|
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
}
|
}
|
||||||
@@ -163,7 +222,7 @@ public class TenetLoaderTest extends AbstractGhidraHeadedDebuggerTest {
|
|||||||
public void testManyLinesBadRegisterNames() throws Exception {
|
public void testManyLinesBadRegisterNames() throws Exception {
|
||||||
// Should fail after 10 lines with bad registers
|
// Should fail after 10 lines with bad registers
|
||||||
final String badTestFile = "pc=0x1,rax=0x1234\n".repeat(TenetLoader.ERROR_THRESHOLD + 1);
|
final String badTestFile = "pc=0x1,rax=0x1234\n".repeat(TenetLoader.ERROR_THRESHOLD + 1);
|
||||||
assertThrows(LoadException.class, () -> this.createTraceWithLoader(badTestFile));
|
assertThrows(LoadException.class, () -> createTraceWithLoader(badTestFile));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -174,7 +233,7 @@ public class TenetLoaderTest extends AbstractGhidraHeadedDebuggerTest {
|
|||||||
public void testManyLinesNoPc() throws Exception {
|
public void testManyLinesNoPc() throws Exception {
|
||||||
// Should fail after {@TenetLoader.ERROR_THRESHOLD} lines with no PC
|
// Should fail after {@TenetLoader.ERROR_THRESHOLD} lines with no PC
|
||||||
final String badTestFile = "r1=0x1234\n".repeat(TenetLoader.ERROR_THRESHOLD + 1);
|
final String badTestFile = "r1=0x1234\n".repeat(TenetLoader.ERROR_THRESHOLD + 1);
|
||||||
assertThrows(LoadException.class, () -> this.createTraceWithLoader(badTestFile));
|
assertThrows(LoadException.class, () -> createTraceWithLoader(badTestFile));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -186,9 +245,16 @@ public class TenetLoaderTest extends AbstractGhidraHeadedDebuggerTest {
|
|||||||
pc=0x1,r0=0x1234,mr=0x1000:0000000000001337
|
pc=0x1,r0=0x1234,mr=0x1000:0000000000001337
|
||||||
pc=0x2,r1=0x4321,mw=0x2000:deadbeefdeadbeef
|
pc=0x2,r1=0x4321,mw=0x2000:deadbeefdeadbeef
|
||||||
pc=0x3,mw=0x1000:87654321,mr=0x2000:c0ffee
|
pc=0x3,mw=0x1000:87654321,mr=0x2000:c0ffee
|
||||||
|
pc=0x4,r1=0x4321
|
||||||
|
pc=0x5,r1=0x4321
|
||||||
|
pc=0x6,r1=0x4321
|
||||||
|
pc=0x7,r1=0x4321
|
||||||
|
pc=0x8,r1=0x4321
|
||||||
|
pc=0x9,r1=0x4321
|
||||||
|
pc=0xa,r1=0x4321
|
||||||
""";
|
""";
|
||||||
|
|
||||||
final Trace trace = this.createTraceWithLoader(testFile);
|
final Trace trace = createTraceWithLoader(testFile);
|
||||||
|
|
||||||
final AddressSpace addrSpace =
|
final AddressSpace addrSpace =
|
||||||
trace.getBaseLanguage().getAddressFactory().getDefaultAddressSpace();
|
trace.getBaseLanguage().getAddressFactory().getDefaultAddressSpace();
|
||||||
@@ -219,8 +285,16 @@ public class TenetLoaderTest extends AbstractGhidraHeadedDebuggerTest {
|
|||||||
final String testFile = """
|
final String testFile = """
|
||||||
pc=0x1,r1=0x1234
|
pc=0x1,r1=0x1234
|
||||||
r2=0x1234
|
r2=0x1234
|
||||||
|
pc=0x3,r1=0x4321
|
||||||
|
pc=0x4,r1=0x4321
|
||||||
|
pc=0x5,r1=0x4321
|
||||||
|
pc=0x6,r1=0x4321
|
||||||
|
pc=0x7,r1=0x4321
|
||||||
|
pc=0x8,r1=0x4321
|
||||||
|
pc=0x9,r1=0x4321
|
||||||
|
pc=0xa,r1=0x4321
|
||||||
""";
|
""";
|
||||||
final Trace trace = this.createTraceWithLoader(testFile);
|
final Trace trace = createTraceWithLoader(testFile);
|
||||||
assertNotNull(trace);
|
assertNotNull(trace);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,13 +306,21 @@ public class TenetLoaderTest extends AbstractGhidraHeadedDebuggerTest {
|
|||||||
final String testFile = """
|
final String testFile = """
|
||||||
pc=0x1,r0=0x1234
|
pc=0x1,r0=0x1234
|
||||||
pc=0x2,r1=0x4321
|
pc=0x2,r1=0x4321
|
||||||
|
pc=0x3,r1=0x4321
|
||||||
|
pc=0x4,r1=0x4321
|
||||||
|
pc=0x5,r1=0x4321
|
||||||
|
pc=0x6,r1=0x4321
|
||||||
|
pc=0x7,r1=0x4321
|
||||||
|
pc=0x8,r1=0x4321
|
||||||
|
pc=0x9,r1=0x4321
|
||||||
|
pc=0xa,r1=0x4321
|
||||||
""";
|
""";
|
||||||
|
|
||||||
final Trace trace = this.createTraceWithLoader(testFile);
|
final Trace trace = createTraceWithLoader(testFile);
|
||||||
|
|
||||||
final TraceStaticMapping staticMapping =
|
final TraceStaticMapping staticMapping =
|
||||||
Unique.assertOne(trace.getStaticMappingManager().getAllEntries());
|
Unique.assertOne(trace.getStaticMappingManager().getAllEntries());
|
||||||
assertEquals(this.program.getImageBase().getUnsignedOffset(),
|
assertEquals(program.getImageBase().getUnsignedOffset(),
|
||||||
staticMapping.getMinTraceAddress().getUnsignedOffset());
|
staticMapping.getMinTraceAddress().getUnsignedOffset());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,9 +333,17 @@ public class TenetLoaderTest extends AbstractGhidraHeadedDebuggerTest {
|
|||||||
slide=0x0
|
slide=0x0
|
||||||
pc=0x1,r0=0x1234
|
pc=0x1,r0=0x1234
|
||||||
pc=0x2,r1=0x4321
|
pc=0x2,r1=0x4321
|
||||||
|
pc=0x3,r1=0x4321
|
||||||
|
pc=0x4,r1=0x4321
|
||||||
|
pc=0x5,r1=0x4321
|
||||||
|
pc=0x6,r1=0x4321
|
||||||
|
pc=0x7,r1=0x4321
|
||||||
|
pc=0x8,r1=0x4321
|
||||||
|
pc=0x9,r1=0x4321
|
||||||
|
pc=0xa,r1=0x4321
|
||||||
""";
|
""";
|
||||||
|
|
||||||
final Trace trace = this.createTraceWithLoader(testFile);
|
final Trace trace = createTraceWithLoader(testFile);
|
||||||
|
|
||||||
final TraceThread thread = Unique.assertOne(trace.getThreadManager().getAllThreads());
|
final TraceThread thread = Unique.assertOne(trace.getThreadManager().getAllThreads());
|
||||||
final TraceMemorySpace regs =
|
final TraceMemorySpace regs =
|
||||||
@@ -261,12 +351,12 @@ public class TenetLoaderTest extends AbstractGhidraHeadedDebuggerTest {
|
|||||||
|
|
||||||
assertNotNull(regs);
|
assertNotNull(regs);
|
||||||
assertEquals("1",
|
assertEquals("1",
|
||||||
regs.getValue(0, this.program.getLanguage().getProgramCounter())
|
regs.getValue(0, program.getLanguage().getProgramCounter())
|
||||||
.getUnsignedValue()
|
.getUnsignedValue()
|
||||||
.toString(16));
|
.toString(16));
|
||||||
|
|
||||||
assertEquals("2",
|
assertEquals("2",
|
||||||
regs.getValue(1, this.program.getLanguage().getProgramCounter())
|
regs.getValue(1, program.getLanguage().getProgramCounter())
|
||||||
.getUnsignedValue()
|
.getUnsignedValue()
|
||||||
.toString(16));
|
.toString(16));
|
||||||
|
|
||||||
@@ -281,13 +371,21 @@ public class TenetLoaderTest extends AbstractGhidraHeadedDebuggerTest {
|
|||||||
slide=0x10000
|
slide=0x10000
|
||||||
pc=0x1,r0=0x1234
|
pc=0x1,r0=0x1234
|
||||||
pc=0x2,r1=0x4321
|
pc=0x2,r1=0x4321
|
||||||
|
pc=0x3,r1=0x4321
|
||||||
|
pc=0x4,r1=0x4321
|
||||||
|
pc=0x5,r1=0x4321
|
||||||
|
pc=0x6,r1=0x4321
|
||||||
|
pc=0x7,r1=0x4321
|
||||||
|
pc=0x8,r1=0x4321
|
||||||
|
pc=0x9,r1=0x4321
|
||||||
|
pc=0xa,r1=0x4321
|
||||||
""";
|
""";
|
||||||
|
|
||||||
final Trace trace = this.createTraceWithLoader(testFile);
|
final Trace trace = createTraceWithLoader(testFile);
|
||||||
|
|
||||||
final TraceStaticMapping staticMapping =
|
final TraceStaticMapping staticMapping =
|
||||||
Unique.assertOne(trace.getStaticMappingManager().getAllEntries());
|
Unique.assertOne(trace.getStaticMappingManager().getAllEntries());
|
||||||
assertEquals(this.program.getImageBase().getUnsignedOffset() + 0x10000,
|
assertEquals(program.getImageBase().getUnsignedOffset() + 0x10000,
|
||||||
staticMapping.getMinTraceAddress().getUnsignedOffset());
|
staticMapping.getMinTraceAddress().getUnsignedOffset());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user