mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
DWARF expression handling refactor
Cleanup logic of expression evaluation, stub out resolution of register values to a callback in case we want to use constant propagation to try to allow successful calculations, and add support for default static values for treating an arch's stack frame register (e.g. RBP) like the static CFA value we already have support for. Add option to decorate params and local vars with their DWARF storage location info. Handle arrays with unspecified element type.
This commit is contained in:
parent
483cd9a799
commit
e908ab6fbf
42 changed files with 2517 additions and 1876 deletions
|
@ -17,8 +17,10 @@ package ghidra.program.model.data;
|
|||
|
||||
import java.io.*;
|
||||
|
||||
import ghidra.util.exception.AssertException;
|
||||
|
||||
/**
|
||||
* Logic for reading LEB128 values.
|
||||
* Logic for reading/writing LEB128 values.
|
||||
* <p>
|
||||
* LEB128 is a variable length integer encoding that uses 7 bits per byte, with the high bit
|
||||
* being reserved as a continuation flag, with the least significant bytes coming first
|
||||
|
@ -150,4 +152,90 @@ public class LEB128 {
|
|||
return read(is, isSigned);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a value into a sequence of LEB128 bytes.
|
||||
*
|
||||
* @param value to encode
|
||||
* @param isSigned boolean flag, if true value is encoded as a signed value, if false value is
|
||||
* encoded as an unsigned value
|
||||
* @return byte array containing the LEB128 bytes of the value (max 10)
|
||||
*/
|
||||
public static byte[] encode(long value, boolean isSigned) {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(MAX_SUPPORTED_LENGTH);
|
||||
write(value, baos, isSigned);
|
||||
return baos.toByteArray();
|
||||
}
|
||||
catch (IOException e) {
|
||||
// should not be able to happen using ByteArrayOutputStream.
|
||||
throw new AssertException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a value to the stream as a sequence of LEB128 bytes.
|
||||
*
|
||||
* @param value to write
|
||||
* @param os {@link OutputStream} to write to
|
||||
* @param isSigned boolean flag, if true value is encoded as a signed value, if false value is
|
||||
* encoded as an unsigned value
|
||||
* @return count of bytes written to stream
|
||||
* @throws IOException if error writing to stream
|
||||
*/
|
||||
public static int write(long value, OutputStream os, boolean isSigned) throws IOException {
|
||||
return isSigned ? writeSigned(value, os) : writeUnsigned(value, os);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a value to the stream as a sequence of LEB128 bytes.
|
||||
*
|
||||
* @param value to write
|
||||
* @param os {@link OutputStream} to write to
|
||||
* @return count of bytes written to stream
|
||||
* @throws IOException if error writing to stream
|
||||
*/
|
||||
public static int writeUnsigned(long value, OutputStream os) throws IOException {
|
||||
int size = 0;
|
||||
boolean done;
|
||||
do {
|
||||
int b = (int) (value & 0x7f);
|
||||
value = value >>> 7;
|
||||
done = value == 0;
|
||||
if (value != 0) {
|
||||
b |= 0x80;
|
||||
}
|
||||
os.write(b);
|
||||
size++;
|
||||
}
|
||||
while (!done);
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a value to the stream as a sequence of LEB128 bytes.
|
||||
*
|
||||
* @param value to write
|
||||
* @param os {@link OutputStream} to write to
|
||||
* @return count of bytes written to stream
|
||||
* @throws IOException if error writing to stream
|
||||
*/
|
||||
public static int writeSigned(long value, OutputStream os) throws IOException {
|
||||
long endingVal = value < 0 ? -1 : 0;
|
||||
int hiBit = value < 0 ? 0x40 : 0;
|
||||
int size = 0;
|
||||
boolean more;
|
||||
do {
|
||||
int b = (int) (value & 0x7f);
|
||||
value = value >> 7;
|
||||
more = value != endingVal || ((b & 0x40) != hiBit);
|
||||
if (more) {
|
||||
b |= 0x80;
|
||||
}
|
||||
os.write(b);
|
||||
size++;
|
||||
}
|
||||
while (more);
|
||||
return size;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -15,11 +15,11 @@
|
|||
*/
|
||||
package ghidra.program.model.data;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.List;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.List;
|
||||
import java.util.stream.LongStream;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
@ -57,38 +57,24 @@ public class LEB128Test extends AbstractGTest {
|
|||
te(0xf_ffff_ffffL, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01), // more than 32 bits to test shifting > 32bits
|
||||
|
||||
// 1 byte
|
||||
te(1L, 0x01),
|
||||
te(63L, 0x3f),
|
||||
te(64L, 0x40),
|
||||
te(1L, 0x01), te(63L, 0x3f), te(64L, 0x40),
|
||||
|
||||
// 1 byte to 2 byte transition
|
||||
te(125L, 0x7d),
|
||||
te(126L, 0x7e),
|
||||
te(127L, 0x7f),
|
||||
te(128L, 0x80, 0x01),
|
||||
te(129L, 0x81, 0x01),
|
||||
te(130L, 0x82, 0x01),
|
||||
te(131L, 0x83, 0x01),
|
||||
te(125L, 0x7d), te(126L, 0x7e), te(127L, 0x7f), te(128L, 0x80, 0x01), te(129L, 0x81, 0x01),
|
||||
te(130L, 0x82, 0x01), te(131L, 0x83, 0x01),
|
||||
|
||||
te(254L, 0xfe, 0x01),
|
||||
te(255L, 0xff, 0x01),
|
||||
te(256L, 0x80, 0x02),
|
||||
te(257L, 0x81, 0x02),
|
||||
te(254L, 0xfe, 0x01), te(255L, 0xff, 0x01), te(256L, 0x80, 0x02), te(257L, 0x81, 0x02),
|
||||
|
||||
// 2 byte to 3 byte transition
|
||||
te(16382L, 0xfe, 0x7f),
|
||||
te(16383L, 0xff, 0x7f),
|
||||
te(16384L, 0x80, 0x80, 0x01),
|
||||
te(16382L, 0xfe, 0x7f), te(16383L, 0xff, 0x7f), te(16384L, 0x80, 0x80, 0x01),
|
||||
te(16385L, 0x81, 0x80, 0x01),
|
||||
|
||||
// 3 byte to 4 byte transition
|
||||
te(2097151L, 0xff, 0xff, 0x7f),
|
||||
te(2097152L, 0x80, 0x80, 0x80, 0x01),
|
||||
te(2097151L, 0xff, 0xff, 0x7f), te(2097152L, 0x80, 0x80, 0x80, 0x01),
|
||||
te(2097153L, 0x81, 0x80, 0x80, 0x01),
|
||||
|
||||
// 4 byte to 5 byte transition
|
||||
te(268435455L, 0xff, 0xff, 0xff, 0x7f),
|
||||
te(268435456L, 0x80, 0x80, 0x80, 0x80, 0x01),
|
||||
te(268435455L, 0xff, 0xff, 0xff, 0x7f), te(268435456L, 0x80, 0x80, 0x80, 0x80, 0x01),
|
||||
te(268435457L, 0x81, 0x80, 0x80, 0x80, 0x01),
|
||||
|
||||
// 5 byte to 6 byte transition
|
||||
|
@ -103,51 +89,29 @@ public class LEB128Test extends AbstractGTest {
|
|||
te(-2130303778817L, 0xff, 0xff, 0xff, 0xff, 0xff, 0x41),
|
||||
|
||||
// 1 byte positive stuff
|
||||
te(0L, 0x00),
|
||||
te(1L, 0x01),
|
||||
te(0L, 0x00), te(1L, 0x01),
|
||||
|
||||
// 1 byte to 2 byte transition (positive)
|
||||
te(63L, 0x3f),
|
||||
te(64L, 0xc0, 0x00),
|
||||
te(65L, 0xc1, 0x00),
|
||||
te(66L, 0xc2, 0x00),
|
||||
te(63L, 0x3f), te(64L, 0xc0, 0x00), te(65L, 0xc1, 0x00), te(66L, 0xc2, 0x00),
|
||||
|
||||
te(126L, 0xfe, 0x00),
|
||||
te(127L, 0xff, 0x00),
|
||||
te(128L, 0x80, 0x01),
|
||||
te(129L, 0x81, 0x01),
|
||||
te(126L, 0xfe, 0x00), te(127L, 0xff, 0x00), te(128L, 0x80, 0x01), te(129L, 0x81, 0x01),
|
||||
|
||||
te(254L, 0xfe, 0x01),
|
||||
te(255L, 0xff, 0x01),
|
||||
te(256L, 0x80, 0x02),
|
||||
te(257L, 0x81, 0x02),
|
||||
te(254L, 0xfe, 0x01), te(255L, 0xff, 0x01), te(256L, 0x80, 0x02), te(257L, 0x81, 0x02),
|
||||
|
||||
// 2 byte to 3 byte transition
|
||||
te(8190L, 0xfe, 0x3f),
|
||||
te(8191L, 0xff, 0x3f),
|
||||
te(8192L, 0x80, 0xc0, 0x00),
|
||||
te(8190L, 0xfe, 0x3f), te(8191L, 0xff, 0x3f), te(8192L, 0x80, 0xc0, 0x00),
|
||||
te(8193L, 0x81, 0xc0, 0x00),
|
||||
|
||||
// 1 byte negative stuff
|
||||
te(-1L, 0x7f),
|
||||
te(-2L, 0x7e),
|
||||
te(-3L, 0x7d),
|
||||
te(-4L, 0x7c),
|
||||
te(-5L, 0x7b),
|
||||
te(-6L, 0x7a),
|
||||
te(-1L, 0x7f), te(-2L, 0x7e), te(-3L, 0x7d), te(-4L, 0x7c), te(-5L, 0x7b), te(-6L, 0x7a),
|
||||
|
||||
// 1 byte to 2 byte transition (negative)
|
||||
te(-64L, 0x40),
|
||||
te(-65L, 0xbf, 0x7f),
|
||||
te(-64L, 0x40), te(-65L, 0xbf, 0x7f),
|
||||
|
||||
te(-127, 0x81, 0x7f),
|
||||
te(-128, 0x80, 0x7f),
|
||||
te(-129, 0xff, 0x7e),
|
||||
te(-127, 0x81, 0x7f), te(-128, 0x80, 0x7f), te(-129, 0xff, 0x7e),
|
||||
|
||||
// 2 byte to 3 byte transition (negative)
|
||||
te(-8191L, 0x81, 0x40),
|
||||
te(-8192L, 0x80, 0x40),
|
||||
te(-8193L, 0xff, 0xbf, 0x7f),
|
||||
te(-8191L, 0x81, 0x40), te(-8192L, 0x80, 0x40), te(-8193L, 0xff, 0xbf, 0x7f),
|
||||
te(-8194L, 0xfe, 0xbf, 0x7f)
|
||||
|
||||
);
|
||||
|
@ -169,13 +133,13 @@ public class LEB128Test extends AbstractGTest {
|
|||
InputStream is = is(te.bytes);
|
||||
long actualValue = LEB128.read(is, signed);
|
||||
int remainder = is.available();
|
||||
assertEquals(String.format(
|
||||
"%s[%d] failed: leb128(%s) != %d. Expected=%d / %x, actual=%d / %x",
|
||||
name, i, NumericUtilities.convertBytesToString(te.bytes), te.expectedValue,
|
||||
te.expectedValue, te.expectedValue, actualValue, actualValue), te.expectedValue,
|
||||
actualValue);
|
||||
assertEquals(String.format("%s[%d] failed: left-over bytes: %d", name, i, remainder),
|
||||
0, is.available());
|
||||
assertEquals(
|
||||
String.format("%s[%d] failed: leb128(%s) != %d. Expected=%d / %x, actual=%d / %x",
|
||||
name, i, NumericUtilities.convertBytesToString(te.bytes), te.expectedValue,
|
||||
te.expectedValue, te.expectedValue, actualValue, actualValue),
|
||||
te.expectedValue, actualValue);
|
||||
assertEquals(String.format("%s[%d] failed: left-over bytes: %d", name, i, remainder), 0,
|
||||
is.available());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -212,4 +176,51 @@ public class LEB128Test extends AbstractGTest {
|
|||
Assert.assertEquals(bytes.length - 10, is.available());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncode() {
|
||||
// positive unsigned
|
||||
LongStream.range(0, 65536 + 10).forEach(this::assertRoundTripUnsigned);
|
||||
LongStream.range(Integer.MAX_VALUE - 1000, Integer.MAX_VALUE + 1000L)
|
||||
.forEach(this::assertRoundTripUnsigned);
|
||||
LongStream.range(Long.MAX_VALUE - 1000, Long.MAX_VALUE)
|
||||
.forEach(this::assertRoundTripUnsigned);
|
||||
|
||||
// positive signed
|
||||
LongStream.range(0, 65536 + 10).forEach(this::assertRoundTripSigned);
|
||||
LongStream.range(Integer.MAX_VALUE - 1000L, Integer.MAX_VALUE + 1000L)
|
||||
.forEach(this::assertRoundTripSigned);
|
||||
LongStream.range(Long.MAX_VALUE - 1000L, Long.MAX_VALUE)
|
||||
.forEach(this::assertRoundTripSigned);
|
||||
|
||||
// negative signed
|
||||
LongStream.range(-65536 - 10, 10).forEach(this::assertRoundTripSigned);
|
||||
LongStream.range(Integer.MIN_VALUE - 1000L, Integer.MIN_VALUE + 1000L)
|
||||
.forEach(this::assertRoundTripSigned);
|
||||
LongStream.range(Long.MIN_VALUE, Long.MIN_VALUE + 1000L)
|
||||
.forEach(this::assertRoundTripSigned);
|
||||
|
||||
}
|
||||
|
||||
private void assertRoundTripUnsigned(long l) {
|
||||
assertRoundTrip(l, false);
|
||||
}
|
||||
|
||||
private void assertRoundTripSigned(long l) {
|
||||
assertRoundTrip(l, true);
|
||||
}
|
||||
|
||||
private void assertRoundTrip(long l, boolean isSigned) {
|
||||
try {
|
||||
byte[] bytes = LEB128.encode(l, isSigned);
|
||||
long decodeResult = LEB128.decode(bytes, 0, isSigned);
|
||||
assertEquals(
|
||||
"%d (0x%x) encoded to %s returned %d (0x%x)".formatted(l, l,
|
||||
NumericUtilities.convertBytesToString(bytes), decodeResult, decodeResult),
|
||||
l, decodeResult);
|
||||
}
|
||||
catch (IOException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue