Convert the unavailable vector to be bit, not byte, based.

https://sourceware.org/ml/gdb-patches/2013-12/msg00144.html

The vector of unavailable parts of a value is currently byte based.  Given
that we can model a value down to the bit level, we can potentially loose
information with the current implementation.  After this patch we model the
unavailable information in bits.

gdb/ChangeLog

	* dwarf2loc.c (read_pieced_value): Mark bits, not bytes
	unavailable, use correct bit length.
	* value.c (struct value): Extend comment on unavailable to
	indicate that it is bit based.
	(value_bits_available): New function.
	(value_bytes_available): Call value_bits_available.
	(value_entirely_available): Check against the bit length, not byte
	length.
	(mark_value_bits_unavailable): New function.
	(mark_value_bytes_unavailable): Move contents to
	mark_value_bits_unavailable, call to same.
	(memcmp_with_bit_offsets): New function.
	(value_available_contents_bits_eq): New function, takes the
	functionality from value_available_contents_eq but uses
	memcmp_with_bit_offsets now, and is bit not byte based.
	(value_available_contents_eq): Move implementation into
	value_available_contents_bits_eq, call to same.
	(value_contents_copy_raw): Work on bits, not bytes.
	(unpack_value_bits_as_long_1): Check availability in bits, not
	bytes.
	* value.h (value_bits_available): Declare new function.
	(mark_value_bits_unavailable): Declare new function.

gdb/testsuite/ChangeLog

	* gdb.trace/unavailable-dwarf-piece.c: New file.
	* gdb.trace/unavailable-dwarf-piece.exp: New file.
This commit is contained in:
Andrew Burgess 2013-12-17 17:18:44 +00:00
parent 4a357820ad
commit bdf2220615
7 changed files with 625 additions and 22 deletions

View File

@ -1,3 +1,28 @@
2013-12-17 Andrew Burgess <aburgess@broadcom.com>
* dwarf2loc.c (read_pieced_value): Mark bits, not bytes
unavailable, use correct bit length.
* value.c (struct value): Extend comment on unavailable to
indicate that it is bit based.
(value_bits_available): New function.
(value_bytes_available): Call value_bits_available.
(value_entirely_available): Check against the bit length, not byte
length.
(mark_value_bits_unavailable): New function.
(mark_value_bytes_unavailable): Move contents to
mark_value_bits_unavailable, call to same.
(memcmp_with_bit_offsets): New function.
(value_available_contents_bits_eq): New function, takes the
functionality from value_available_contents_eq but uses
memcmp_with_bit_offsets now, and is bit not byte based.
(value_available_contents_eq): Move implementation into
value_available_contents_bits_eq, call to same.
(value_contents_copy_raw): Work on bits, not bytes.
(unpack_value_bits_as_long_1): Check availability in bits, not
bytes.
* value.h (value_bits_available): Declare new function.
(mark_value_bits_unavailable): Declare new function.
2013-12-16 Pierre Muller <muller@sourceware.org>
Fix compilation error for cygwin native build.

View File

@ -1709,7 +1709,7 @@ read_pieced_value (struct value *v)
if (optim)
set_value_optimized_out (v, 1);
if (unavail)
mark_value_bytes_unavailable (v, offset, this_size);
mark_value_bits_unavailable (v, offset, this_size_bits);
}
}
else

View File

@ -1,3 +1,8 @@
2013-12-17 Andrew Burgess <aburgess@broadcom.com>
* gdb.trace/unavailable-dwarf-piece.c: New file.
* gdb.trace/unavailable-dwarf-piece.exp: New file.
2013-12-15 Yao Qi <yao@codesourcery.com>
* gdb.perf/skip-prologue.c: New.

View File

@ -0,0 +1,86 @@
/* This testcase is part of GDB, the GNU debugger.
Copyright 2013 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
struct s
{
unsigned char a;
unsigned char b;
unsigned char c;
};
struct t
{
/* First, a complete byte. */
unsigned char a;
/* Next, 8 single bits. */
unsigned char b : 1;
unsigned char c : 1;
unsigned char d : 1;
unsigned char e : 1;
unsigned char f : 1;
unsigned char g : 1;
unsigned char h : 1;
unsigned char i : 1;
/* Now another byte. */
unsigned char j;
};
void
end (void)
{
/* Nothing. */
}
void
dummy (void)
{
/* Nothing. */
}
int
foo (struct s x, struct s y, struct s z)
{
dummy ();
asm (".global foo_end_lbl\nfoo_end_lbl:");
return 0;
}
int
bar (struct t x, struct t y, struct t z)
{
dummy ();
asm (".global bar_end_lbl\nbar_end_lbl:");
return 0;
}
int
main (void)
{
struct s v = { 0, 1, 2 };
struct t w = { 5, 0, 1, 0, 1, 0, 1, 0, 1, 7 };
int ans;
ans = foo (v, v, v);
end ();
ans = bar (w, w, w);
end ();
return ans;
}

View File

@ -0,0 +1,334 @@
# Copyright (C) 2013 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
load_lib "trace-support.exp"
load_lib dwarf.exp
if {![dwarf2_support]} {
return 0
}
standard_testfile .c
set asm_file "${testfile}-dbg.s"
set opts {}
if { [gdb_compile ${srcdir}/${subdir}/${srcfile} ${binfile}1.o \
object {nodebug}] != "" } {
return -1
}
Dwarf::assemble $asm_file {
declare_labels uchar_label struct_s_label foo_label struct_t_label bar_label
cu {} {
compile_unit {{language @DW_LANG_C}} {
uchar_label: DW_TAG_base_type {
{name "unsigned char"}
{byte_size 1 DW_FORM_sdata}
{encoding @DW_ATE_unsigned_char}
}
struct_s_label: DW_TAG_structure_type {
{name s}
{byte_size 3 DW_FORM_sdata}
{decl_file 1}
{decl_line 1}
} {
DW_TAG_member {
{name a}
{type :$uchar_label}
{data_member_location {
DW_OP_plus_uconst 0
} SPECIAL_expr}
}
DW_TAG_member {
{name b}
{type :$uchar_label}
{data_member_location {
DW_OP_plus_uconst 1
} SPECIAL_expr}
}
DW_TAG_member {
{name c}
{type :$uchar_label}
{data_member_location {
DW_OP_plus_uconst 2
} SPECIAL_expr}
}
}
struct_t_label: DW_TAG_structure_type {
{name t}
{byte_size 3 DW_FORM_sdata}
{decl_file 1}
{decl_line 1}
} {
DW_TAG_member {
{name a}
{type :$uchar_label}
{data_member_location {
DW_OP_plus_uconst 0
} SPECIAL_expr}
}
DW_TAG_member {
{name b}
{type :$uchar_label}
{byte_size 1 DW_FORM_sdata}
{bit_size 1 DW_FORM_sdata}
{bit_offset 7 DW_FORM_sdata}
{data_member_location {
DW_OP_plus_uconst 1
} SPECIAL_expr}
}
DW_TAG_member {
{name c}
{type :$uchar_label}
{byte_size 1 DW_FORM_sdata}
{bit_size 1 DW_FORM_sdata}
{bit_offset 6 DW_FORM_sdata}
{data_member_location {
DW_OP_plus_uconst 1
} SPECIAL_expr}
}
DW_TAG_member {
{name d}
{type :$uchar_label}
{byte_size 1 DW_FORM_sdata}
{bit_size 1 DW_FORM_sdata}
{bit_offset 5 DW_FORM_sdata}
{data_member_location {
DW_OP_plus_uconst 1
} SPECIAL_expr}
}
DW_TAG_member {
{name e}
{type :$uchar_label}
{byte_size 1 DW_FORM_sdata}
{bit_size 1 DW_FORM_sdata}
{bit_offset 4 DW_FORM_sdata}
{data_member_location {
DW_OP_plus_uconst 1
} SPECIAL_expr}
}
DW_TAG_member {
{name f}
{type :$uchar_label}
{byte_size 1 DW_FORM_sdata}
{bit_size 1 DW_FORM_sdata}
{bit_offset 3 DW_FORM_sdata}
{data_member_location {
DW_OP_plus_uconst 1
} SPECIAL_expr}
}
DW_TAG_member {
{name g}
{type :$uchar_label}
{byte_size 1 DW_FORM_sdata}
{bit_size 1 DW_FORM_sdata}
{bit_offset 2 DW_FORM_sdata}
{data_member_location {
DW_OP_plus_uconst 1
} SPECIAL_expr}
}
DW_TAG_member {
{name h}
{type :$uchar_label}
{byte_size 1 DW_FORM_sdata}
{bit_size 1 DW_FORM_sdata}
{bit_offset 1 DW_FORM_sdata}
{data_member_location {
DW_OP_plus_uconst 1
} SPECIAL_expr}
}
DW_TAG_member {
{name i}
{type :$uchar_label}
{byte_size 1 DW_FORM_sdata}
{bit_size 1 DW_FORM_sdata}
{bit_offset 0 DW_FORM_sdata}
{data_member_location {
DW_OP_plus_uconst 1
} SPECIAL_expr}
}
DW_TAG_member {
{name j}
{type :$uchar_label}
{data_member_location {
DW_OP_plus_uconst 2
} SPECIAL_expr}
}
}
DW_TAG_subprogram {
{name foo}
{decl_file 1}
{low_pc foo addr}
{high_pc foo_end_lbl addr}
} {
DW_TAG_formal_parameter {
{type :$struct_s_label}
{name x}
{location {
DW_OP_lit0
DW_OP_stack_value
DW_OP_piece 2
DW_OP_reg0
DW_OP_piece 1
} SPECIAL_expr}
}
DW_TAG_formal_parameter {
{type :$struct_s_label}
{name y}
{location {
DW_OP_lit0
DW_OP_stack_value
DW_OP_piece 1
DW_OP_reg0
DW_OP_piece 1
DW_OP_lit0
DW_OP_stack_value
DW_OP_piece 1
} SPECIAL_expr}
}
DW_TAG_formal_parameter {
{type :$struct_s_label}
{name z}
{location {
DW_OP_reg0
DW_OP_piece 1
DW_OP_lit0
DW_OP_stack_value
DW_OP_piece 2
} SPECIAL_expr}
}
}
DW_TAG_subprogram {
{name bar}
{decl_file 1}
{low_pc bar addr}
{high_pc bar_end_lbl addr}
} {
DW_TAG_formal_parameter {
{type :$struct_t_label}
{name x}
{location {
DW_OP_lit0
DW_OP_stack_value
DW_OP_piece 1
DW_OP_reg0
DW_OP_bit_piece 1 0
DW_OP_lit0
DW_OP_stack_value
DW_OP_bit_piece 7 0
DW_OP_lit0
DW_OP_stack_value
DW_OP_piece 1
} SPECIAL_expr}
}
DW_TAG_formal_parameter {
{type :$struct_t_label}
{name y}
{location {
DW_OP_lit0
DW_OP_stack_value
DW_OP_piece 1
DW_OP_lit0
DW_OP_stack_value
DW_OP_bit_piece 3 0
DW_OP_reg0
DW_OP_bit_piece 1 0
DW_OP_lit0
DW_OP_stack_value
DW_OP_bit_piece 4 0
DW_OP_lit0
DW_OP_stack_value
DW_OP_piece 1
} SPECIAL_expr}
}
DW_TAG_formal_parameter {
{type :$struct_t_label}
{name z}
{location {
DW_OP_lit0
DW_OP_stack_value
DW_OP_piece 1
DW_OP_lit0
DW_OP_stack_value
DW_OP_bit_piece 7 0
DW_OP_reg0
DW_OP_bit_piece 1 0
DW_OP_lit0
DW_OP_stack_value
DW_OP_piece 1
} SPECIAL_expr}
}
}
}
}
}
if { [gdb_compile ${asm_file} ${binfile}2.o object {nodebug}] != "" } {
return -1
}
if { [gdb_compile [list ${binfile}1.o ${binfile}2.o] ${binfile} \
executable {}] != "" } {
return -1
}
clean_restart ${testfile}
if ![runto_main] {
return -1
}
if ![gdb_target_supports_trace] {
unsupported "target does not support trace"
return -1
}
gdb_breakpoint "end"
with_test_prefix "tracing foo" {
gdb_test "trace foo" ".*"
gdb_test_no_output "tstart"
gdb_test "continue" "Continuing\\.\[ \r\n\]+Breakpoint.*"
gdb_test_no_output "tstop"
gdb_test "tfind 0" "Found trace frame 0, tracepoint .*"
gdb_test "p/d x" "\\\$${decimal} = {a = 0, b = 0, c = <unavailable>}"
gdb_test "p/d y" "\\\$${decimal} = {a = 0, b = <unavailable>, c = 0}"
gdb_test "p/d z" "\\\$${decimal} = {a = <unavailable>, b = 0, c = 0}"
gdb_test "tfind none" "No longer looking at any trace frame.*"
}
with_test_prefix "tracing bar" {
gdb_test "trace bar" ".*"
gdb_test_no_output "tstart"
gdb_test "continue" "Continuing\\.\[ \r\n\]+Breakpoint.*"
gdb_test_no_output "tstop"
gdb_test "tfind 0" "Found trace frame 0, tracepoint .*"
gdb_test "p/d x" "\\\$${decimal} = {a = 0, b = <unavailable>, c = 0, d = 0, e = 0, f = 0, g = 0, h = 0, i = 0, j = 0}"
gdb_test "p/d y" "\\\$${decimal} = {a = 0, b = 0, c = 0, d = 0, e = <unavailable>, f = 0, g = 0, h = 0, i = 0, j = 0}"
gdb_test "p/d z" "\\\$${decimal} = {a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0, h = 0, i = <unavailable>, j = 0}"
gdb_test "tfind none" "No longer looking at any trace frame.*"
}

View File

@ -333,18 +333,27 @@ struct value
/* Unavailable ranges in CONTENTS. We mark unavailable ranges,
rather than available, since the common and default case is for a
value to be available. This is filled in at value read time. */
value to be available. This is filled in at value read time. The
unavailable ranges are tracked in bits. */
VEC(range_s) *unavailable;
};
int
value_bytes_available (const struct value *value, int offset, int length)
value_bits_available (const struct value *value, int offset, int length)
{
gdb_assert (!value->lazy);
return !ranges_contain (value->unavailable, offset, length);
}
int
value_bytes_available (const struct value *value, int offset, int length)
{
return value_bits_available (value,
offset * TARGET_CHAR_BIT,
length * TARGET_CHAR_BIT);
}
int
value_entirely_available (struct value *value)
{
@ -371,7 +380,8 @@ value_entirely_unavailable (struct value *value)
struct range *t = VEC_index (range_s, value->unavailable, 0);
if (t->offset == 0
&& t->length == TYPE_LENGTH (value_enclosing_type (value)))
&& t->length == (TARGET_CHAR_BIT *
TYPE_LENGTH (value_enclosing_type (value))))
return 1;
}
@ -379,7 +389,7 @@ value_entirely_unavailable (struct value *value)
}
void
mark_value_bytes_unavailable (struct value *value, int offset, int length)
mark_value_bits_unavailable (struct value *value, int offset, int length)
{
range_s newr;
int i;
@ -543,6 +553,14 @@ mark_value_bytes_unavailable (struct value *value, int offset, int length)
}
}
void
mark_value_bytes_unavailable (struct value *value, int offset, int length)
{
mark_value_bits_unavailable (value,
offset * TARGET_CHAR_BIT,
length * TARGET_CHAR_BIT);
}
/* Find the first range in RANGES that overlaps the range defined by
OFFSET and LENGTH, starting at element POS in the RANGES vector,
Returns the index into RANGES where such overlapping range was
@ -562,10 +580,118 @@ find_first_range_overlap (VEC(range_s) *ranges, int pos,
return -1;
}
int
value_available_contents_eq (const struct value *val1, int offset1,
const struct value *val2, int offset2,
int length)
/* Compare LENGTH_BITS of memory at PTR1 + OFFSET1_BITS with the memory at
PTR2 + OFFSET2_BITS. Return 0 if the memory is the same, otherwise
return non-zero.
It must always be the case that:
OFFSET1_BITS % TARGET_CHAR_BIT == OFFSET2_BITS % TARGET_CHAR_BIT
It is assumed that memory can be accessed from:
PTR + (OFFSET_BITS / TARGET_CHAR_BIT)
to:
PTR + ((OFFSET_BITS + LENGTH_BITS + TARGET_CHAR_BIT - 1)
/ TARGET_CHAR_BIT) */
static int
memcmp_with_bit_offsets (const gdb_byte *ptr1, size_t offset1_bits,
const gdb_byte *ptr2, size_t offset2_bits,
size_t length_bits)
{
gdb_assert (offset1_bits % TARGET_CHAR_BIT
== offset2_bits % TARGET_CHAR_BIT);
if (offset1_bits % TARGET_CHAR_BIT != 0)
{
size_t bits;
gdb_byte mask, b1, b2;
/* The offset from the base pointers PTR1 and PTR2 is not a complete
number of bytes. A number of bits up to either the next exact
byte boundary, or LENGTH_BITS (which ever is sooner) will be
compared. */
bits = TARGET_CHAR_BIT - offset1_bits % TARGET_CHAR_BIT;
gdb_assert (bits < sizeof (mask) * TARGET_CHAR_BIT);
mask = (1 << bits) - 1;
if (length_bits < bits)
{
mask &= ~(gdb_byte) ((1 << (bits - length_bits)) - 1);
bits = length_bits;
}
/* Now load the two bytes and mask off the bits we care about. */
b1 = *(ptr1 + offset1_bits / TARGET_CHAR_BIT) & mask;
b2 = *(ptr2 + offset2_bits / TARGET_CHAR_BIT) & mask;
if (b1 != b2)
return 1;
/* Now update the length and offsets to take account of the bits
we've just compared. */
length_bits -= bits;
offset1_bits += bits;
offset2_bits += bits;
}
if (length_bits % TARGET_CHAR_BIT != 0)
{
size_t bits;
size_t o1, o2;
gdb_byte mask, b1, b2;
/* The length is not an exact number of bytes. After the previous
IF.. block then the offsets are byte aligned, or the
length is zero (in which case this code is not reached). Compare
a number of bits at the end of the region, starting from an exact
byte boundary. */
bits = length_bits % TARGET_CHAR_BIT;
o1 = offset1_bits + length_bits - bits;
o2 = offset2_bits + length_bits - bits;
gdb_assert (bits < sizeof (mask) * TARGET_CHAR_BIT);
mask = ((1 << bits) - 1) << (TARGET_CHAR_BIT - bits);
gdb_assert (o1 % TARGET_CHAR_BIT == 0);
gdb_assert (o2 % TARGET_CHAR_BIT == 0);
b1 = *(ptr1 + o1 / TARGET_CHAR_BIT) & mask;
b2 = *(ptr2 + o2 / TARGET_CHAR_BIT) & mask;
if (b1 != b2)
return 1;
length_bits -= bits;
}
if (length_bits > 0)
{
/* We've now taken care of any stray "bits" at the start, or end of
the region to compare, the remainder can be covered with a simple
memcmp. */
gdb_assert (offset1_bits % TARGET_CHAR_BIT == 0);
gdb_assert (offset2_bits % TARGET_CHAR_BIT == 0);
gdb_assert (length_bits % TARGET_CHAR_BIT == 0);
return memcmp (ptr1 + offset1_bits / TARGET_CHAR_BIT,
ptr2 + offset2_bits / TARGET_CHAR_BIT,
length_bits / TARGET_CHAR_BIT);
}
/* Length is zero, regions match. */
return 0;
}
/* Helper function for value_available_contents_eq. The only difference is
that this function is bit rather than byte based.
Compare LENGTH bits of VAL1's contents starting at OFFSET1 bits with
LENGTH bits of VAL2's contents starting at OFFSET2 bits. Return true
if the available bits match. */
static int
value_available_contents_bits_eq (const struct value *val1, int offset1,
const struct value *val2, int offset2,
int length)
{
int idx1 = 0, idx2 = 0;
@ -585,9 +711,9 @@ value_available_contents_eq (const struct value *val1, int offset1,
/* The usual case is for both values to be completely available. */
if (idx1 == -1 && idx2 == -1)
return (memcmp (val1->contents + offset1,
val2->contents + offset2,
length) == 0);
return (memcmp_with_bit_offsets (val1->contents, offset1,
val2->contents, offset2,
length) == 0);
/* The contents only match equal if the available set matches as
well. */
else if (idx1 == -1 || idx2 == -1)
@ -620,9 +746,8 @@ value_available_contents_eq (const struct value *val1, int offset1,
return 0;
/* Compare the _available_ contents. */
if (memcmp (val1->contents + offset1,
val2->contents + offset2,
l1) != 0)
if (memcmp_with_bit_offsets (val1->contents, offset1,
val2->contents, offset2, l1) != 0)
return 0;
length -= h1;
@ -633,6 +758,16 @@ value_available_contents_eq (const struct value *val1, int offset1,
return 1;
}
int
value_available_contents_eq (const struct value *val1, int offset1,
const struct value *val2, int offset2,
int length)
{
return value_available_contents_bits_eq (val1, offset1 * TARGET_CHAR_BIT,
val2, offset2 * TARGET_CHAR_BIT,
length * TARGET_CHAR_BIT);
}
/* Prototypes for local functions. */
static void show_values (char *, int);
@ -972,6 +1107,7 @@ value_contents_copy_raw (struct value *dst, int dst_offset,
{
range_s *r;
int i;
int src_bit_offset, dst_bit_offset, bit_length;
/* A lazy DST would make that this copy operation useless, since as
soon as DST's contents were un-lazied (by a later value_contents
@ -990,17 +1126,20 @@ value_contents_copy_raw (struct value *dst, int dst_offset,
length);
/* Copy the meta-data, adjusted. */
src_bit_offset = src_offset * TARGET_CHAR_BIT;
dst_bit_offset = dst_offset * TARGET_CHAR_BIT;
bit_length = length * TARGET_CHAR_BIT;
for (i = 0; VEC_iterate (range_s, src->unavailable, i, r); i++)
{
ULONGEST h, l;
l = max (r->offset, src_offset);
h = min (r->offset + r->length, src_offset + length);
l = max (r->offset, src_bit_offset);
h = min (r->offset + r->length, src_bit_offset + bit_length);
if (l < h)
mark_value_bytes_unavailable (dst,
dst_offset + (l - src_offset),
h - l);
mark_value_bits_unavailable (dst,
dst_bit_offset + (l - src_bit_offset),
h - l);
}
}
@ -2884,8 +3023,8 @@ unpack_value_bits_as_long_1 (struct type *field_type, const gdb_byte *valaddr,
read_offset = bitpos / 8;
if (original_value != NULL
&& !value_bytes_available (original_value, embedded_offset + read_offset,
bytes_read))
&& !value_bits_available (original_value, embedded_offset + bitpos,
bitsize))
return 0;
val = extract_unsigned_integer (valaddr + embedded_offset + read_offset,

View File

@ -436,6 +436,14 @@ extern int value_bits_synthetic_pointer (const struct value *value,
extern int value_bytes_available (const struct value *value,
int offset, int length);
/* Given a value, determine whether the contents bits starting at
OFFSET and extending for LENGTH bits are available. This returns
nonzero if all bits in the given range are available, zero if any
bit is unavailable. */
extern int value_bits_available (const struct value *value,
int offset, int length);
/* Like value_bytes_available, but return false if any byte in the
whole object is unavailable. */
extern int value_entirely_available (struct value *value);
@ -450,6 +458,12 @@ extern int value_entirely_unavailable (struct value *value);
extern void mark_value_bytes_unavailable (struct value *value,
int offset, int length);
/* Mark VALUE's content bits starting at OFFSET and extending for
LENGTH bits as unavailable. */
extern void mark_value_bits_unavailable (struct value *value,
int offset, int length);
/* Compare LENGTH bytes of VAL1's contents starting at OFFSET1 with
LENGTH bytes of VAL2's contents starting at OFFSET2.