Skip to content

Commit 4d73489

Browse files
authored
Big endian build to test bitfields (#20)
* adding tests for base types (char) and pre-check which checks algorithm to access bitfields * corrected read/write for Big endian bitfields * added job to check on big endian platform (qemu based)
1 parent d084019 commit 4d73489

File tree

6 files changed

+210
-23
lines changed

6 files changed

+210
-23
lines changed

.github/workflows/buildAndTest.yaml

+43-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,47 @@ jobs:
2727
- name: Examples test
2828
run: make test all DWARF_VER_FORCE=${{matrix.dwarf-version}}
2929

30+
ubuntu_big_endian:
31+
# pretty slow since it's running on qemu. we may disable it periodically
32+
# taken from https://github.com/marketplace/actions/run-on-architecture
33+
# s390x is big endian with Ubuntu_latest which
34+
# also has go-lang for linux: https://go.dev/dl/
35+
# https://go.dev/dl/go1.23.0.linux-s390x.tar.gz
36+
# GO_TELEMETRY_CHILD=off
37+
# The host should always be Linux.
38+
runs-on: ubuntu-22.04
39+
name: ubuntu (s390x - big endian)
40+
steps:
41+
- uses: actions/checkout@v4
42+
- name: Download Go
43+
run: curl -fsSL -o go.tar.gz https://go.dev/dl/go1.22.0.linux-s390x.tar.gz
44+
- uses: uraimo/run-on-arch-action@v2
45+
name: Run commands
46+
id: runcmd
47+
with:
48+
arch: s390x
49+
distro: ubuntu_latest
50+
51+
# Not required, but speeds up builds by storing container images in
52+
# a GitHub package registry.
53+
githubToken: ${{ github.token }}
54+
55+
# Set an output parameter `uname` for use in subsequent steps
56+
run: |
57+
set -x
58+
uname -a
59+
apt update && apt-get install -y ca-certificates pkg-config make gcc g++ check # todo: libffi-dev
60+
61+
tar -C /usr/local -xzf go.tar.gz && rm go.tar.gz
62+
export PATH=$PATH:/usr/local/go/bin
63+
go version
64+
65+
export GO111MODULE=on
66+
go test -cover ./...
67+
go build
68+
69+
make test all
70+
3071
macos:
3172
runs-on: macos-latest
3273
strategy:
@@ -70,7 +111,7 @@ jobs:
70111
msystem: ${{matrix.sys}}
71112
update: true
72113
install: base-devel git pkg-config mingw-w64-${{matrix.env}}-check mingw-w64-${{matrix.env}}-toolchain
73-
path-type: inherit # to be able to find go
114+
path-type: inherit # to be able to find go in PATH
74115
- name: Checkout code
75116
uses: actions/checkout@v3
76117
- name: Test
@@ -80,4 +121,4 @@ jobs:
80121
- name: Build
81122
run: go build
82123
- name: Examples test
83-
run: make test all
124+
run: make test all

src/entry.c

+1-2
Original file line numberDiff line numberDiff line change
@@ -443,11 +443,10 @@ int metac_entry_member_bitfield_offsets(metac_entry_t *p_memb_entry,
443443
*p_bit_offset = _data_bit_offset & 0x7;
444444
} else if (p_memb_entry->member_info.p_data_bit_offset != NULL) { /* dwarf 4 */
445445
// in data
446-
447446
metac_offset_t _data_bit_offset = *p_memb_entry->member_info.p_data_bit_offset;
448447
*p_bit_size = p_memb_entry->member_info.p_bit_size != NULL?(*p_memb_entry->member_info.p_bit_size): 0;
449-
// out
450448

449+
// out
451450
*p_byte_offset = _data_bit_offset >> 3;
452451
*p_bit_offset = _data_bit_offset & 0x7;
453452
}

src/value.c

+6-16
Original file line numberDiff line numberDiff line change
@@ -133,16 +133,11 @@ static int _entry_bitfield_read(metac_entry_t *p_memb_entry, void * base_addr, v
133133
// TODO: make it without extra copy if possible
134134
// TODO: need to make it for both endians
135135
assert(bit_size != 0);
136-
memcpy(buf, base_addr, (bit_offset + bit_size-1)/8 +1);
136+
memcpy(buf, base_addr, (bit_offset + bit_size - 1)/8 + 1);
137137
base_addr = buf;
138138
} else {
139-
assert(metac_entry_parent_count(p_memb_entry) == 1);
140-
metac_entry_t * p_memb_parent = metac_entry_parent_entry(p_memb_entry, 0);
141-
assert(p_memb_parent != NULL);
142-
metac_size_t parent_byte_size = 0;
143-
_check_(metac_entry_byte_size(p_memb_parent, &parent_byte_size) != 0, -(EFAULT));
144-
assert(parent_byte_size > 0);
145-
base_addr += parent_byte_size - byte_offset;
139+
base_addr += byte_offset;
140+
bit_offset = 8 * var_size - (bit_offset + bit_size);
146141
}
147142

148143
#define _read_(_type_) \
@@ -194,16 +189,11 @@ static int _entry_bitfield_write(metac_entry_t *p_memb_entry, void * base_addr,
194189
// TODO: need to make it for both endians
195190
assert(bit_size != 0);
196191
base_addr_orig = base_addr;
197-
memcpy(buf, base_addr, (bit_offset + bit_size-1)/8 +1);
192+
memcpy(buf, base_addr, (bit_offset + bit_size - 1)/8 + 1);
198193
base_addr = buf;
199194
} else {
200-
assert(metac_entry_parent_count(p_memb_entry) == 1);
201-
metac_entry_t * p_memb_parent = metac_entry_parent_entry(p_memb_entry, 0);
202-
assert(p_memb_parent != NULL);
203-
metac_size_t parent_byte_size = 0;
204-
_check_(metac_entry_byte_size(p_memb_parent, &parent_byte_size) != 0, -(EFAULT));
205-
assert(parent_byte_size > 0);
206-
base_addr += parent_byte_size - byte_offset;
195+
base_addr += byte_offset;
196+
bit_offset = 8 * var_size - (bit_offset + bit_size);
207197
}
208198

209199
#define _write_(_type_) \

src/value_base_type.c

+6-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ dsprintf_render_with_buf(64)
1111
#endif
1212

1313
metac_flag_t metac_entry_is_char(metac_entry_t * p_entry) {
14-
return metac_entry_check_base_type(p_entry, "char", METAC_ENC_signed_char, sizeof(char)) == 0;
14+
return metac_entry_check_base_type(p_entry, "char", METAC_ENC_signed_char, sizeof(char)) == 0 ||
15+
metac_entry_check_base_type(p_entry, "char", METAC_ENC_unsigned_char, sizeof(char)) == 0; // some platforms are doing this
1516
}
1617
metac_flag_t metac_entry_is_uchar(metac_entry_t * p_entry) {
1718
return metac_entry_check_base_type(p_entry, "unsigned char", METAC_ENC_unsigned_char, sizeof(unsigned char)) == 0;
@@ -115,7 +116,8 @@ metac_flag_t metac_value_is_ldouble_complex(metac_value_t * p_val) {
115116
}
116117
/**/
117118
int metac_value_char(metac_value_t * p_val, char *p_var) {
118-
return metac_value_base_type(p_val, "char", METAC_ENC_signed_char, (void*)p_var, sizeof(*p_var));
119+
return (metac_value_base_type(p_val, "char", METAC_ENC_signed_char, (void*)p_var, sizeof(*p_var)) == 0 ||
120+
metac_value_base_type(p_val, "char", METAC_ENC_unsigned_char, (void*)p_var, sizeof(*p_var)) == 0)?0:-(EFAULT);
119121
}
120122
int metac_value_uchar(metac_value_t * p_val, unsigned char *p_var) {
121123
return metac_value_base_type(p_val, "unsigned char", METAC_ENC_unsigned_char, (void*)p_var, sizeof(*p_var));
@@ -167,7 +169,8 @@ int metac_value_ldouble_complex(metac_value_t * p_val, long double complex *p_va
167169
}
168170
/* */
169171
int metac_value_set_char(metac_value_t * p_val, char var) {
170-
return metac_value_set_base_type(p_val, "char", METAC_ENC_signed_char, &var, sizeof(var));
172+
return (metac_value_set_base_type(p_val, "char", METAC_ENC_signed_char, &var, sizeof(var)) == 0 ||
173+
metac_value_set_base_type(p_val, "char", METAC_ENC_unsigned_char, &var, sizeof(var)) == 0)?0:-(EFAULT);
171174
}
172175
int metac_value_set_uchar(metac_value_t * p_val, unsigned char var) {
173176
return metac_value_set_base_type(p_val, "unsigned char", METAC_ENC_unsigned_char, &var, sizeof(var));

src/value_base_type_test.c

+13
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,19 @@
1717
static void test_char(metac_value_t * p_val, char * p_actual_data, metac_flag_t expected_write_err) {
1818
fail_unless(p_val != NULL, "couldn't create p_val");
1919

20+
metac_entry_t *p_entry = metac_entry_final_entry(metac_value_entry(p_val), NULL);
21+
fail_unless(metac_entry_is_base_type(p_entry)!=0);
22+
fail_unless(metac_entry_name(p_entry) != 0, "name is null");
23+
fail_unless(strcmp("char", metac_entry_name(p_entry)) == 0, "expected char, got %s", metac_entry_name(p_entry));
24+
metac_size_t sz;
25+
fail_unless(metac_entry_byte_size(p_entry, &sz) == 0);
26+
fail_unless(sz == sizeof(char), "sz got %d, expected %d", (int)sz, (int)sizeof(char));
27+
metac_encoding_t enc;
28+
fail_unless(metac_entry_base_type_encoding(p_entry, &enc)==0);
29+
fail_unless(enc == METAC_ENC_signed_char || enc == METAC_ENC_unsigned_char, "sz got %d, expected %d or %d",
30+
(int)enc, (int)METAC_ENC_signed_char, (int)METAC_ENC_unsigned_char);
31+
32+
2033
char target;
2134
fail_unless(metac_value_is_char(p_val) != 0, "0 metac_value_is_char returned error");
2235
fail_unless(metac_value_char(p_val, &target) == 0, "0 metac_value_char returned error");

src/value_bitfields_test.c

+141
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,147 @@
1111
#include "value_base_type.c"
1212
#include "value_with_args.c"
1313

14+
// uncomment the next line if the test doesn't work
15+
// #define PRECHECK_DEBUG
16+
struct {
17+
char a:2;
18+
char b:3;
19+
char c:3; // goes beyond the first char (shoud be moved to the next byte by compiler)
20+
int x: 10;
21+
long z: 22;
22+
}precheck_struct;
23+
METAC_GSYM_LINK(precheck_struct);
24+
25+
#define DUMP_MEM(_start_, _size_) \
26+
do { \
27+
int i; \
28+
unsigned char * p = (unsigned char *)_start_; \
29+
for (i=0; i<(_size_); i++) { \
30+
fprintf(stderr, "%02x ", (int)*p++); \
31+
} \
32+
fprintf(stderr, "\n"); \
33+
}while(0)
34+
35+
METAC_START_TEST(pre_check) {
36+
metac_value_t * p_val = METAC_VALUE_FROM_LINK(precheck_struct);
37+
char target;
38+
fail_unless(metac_value_char(p_val, &target) != 0, "metac_value_char must return error because p_val isn't char");
39+
metac_num_t count = metac_value_member_count(p_val);
40+
fail_unless(count != 0, "got empty struct");
41+
for (metac_num_t fld_id = 0; fld_id < count; ++fld_id) {
42+
metac_value_t * p_mem_val = metac_new_value_by_member_id(p_val, fld_id);
43+
metac_entry_t * p_mem_entry = p_mem_val->p_entry;
44+
45+
metac_entry_t * p_final_entry = metac_entry_final_entry(p_mem_entry, NULL);
46+
metac_encoding_t enc;
47+
fail_unless(metac_entry_base_type_encoding(p_final_entry, &enc)==0);
48+
49+
fail_unless(p_mem_entry->kind == METAC_KND_member, "must be member info kind");
50+
metac_offset_t byte_offset;
51+
metac_offset_t bit_offset;
52+
metac_offset_t bit_size;
53+
if (p_mem_entry->member_info.p_bit_offset != NULL) {
54+
#ifdef PRECHECK_DEBUG
55+
fprintf(stderr, "DWARF 3, LE %d\n", (int)IS_LITTLE_ENDIAN);
56+
#endif
57+
metac_offset_t _bit_size = (p_mem_entry->member_info.p_bit_size?(*p_mem_entry->member_info.p_bit_size):0),
58+
_bit_offset = (p_mem_entry->member_info.p_bit_offset?(*p_mem_entry->member_info.p_bit_offset):0),
59+
_byte_size = (p_mem_entry->member_info.p_byte_size?(*p_mem_entry->member_info.p_byte_size):0);
60+
metac_offset_t _data_bit_offset = (p_mem_entry->member_info.byte_offset + _byte_size) * 8 - (_bit_offset + _bit_size);
61+
byte_offset = _data_bit_offset >> 3;
62+
bit_offset = _data_bit_offset & 0x7;
63+
bit_size = _bit_size;
64+
#ifdef PRECHECK_DEBUG
65+
fprintf(stderr, "fld %4s, bit_offset %d, bit_size %d, location %d byte_size %d=> byte_offset %d, bit_offset %d bit_size %d\n",
66+
p_mem_entry->name, (int)(_bit_offset), (int)_bit_size, (int)p_mem_entry->member_info.byte_offset, (int)_byte_size,
67+
(int)byte_offset, (int)bit_offset, (int)bit_size);
68+
#endif
69+
} else if (p_mem_entry->member_info.p_data_bit_offset != NULL) { // test DWARF 4 approach
70+
#ifdef PRECHECK_DEBUG
71+
fprintf(stderr, "DWARF 4, LE %d\n", (int)IS_LITTLE_ENDIAN);
72+
#endif
73+
metac_offset_t _data_bit_offset = *p_mem_entry->member_info.p_data_bit_offset;
74+
metac_offset_t _bit_size = p_mem_entry->member_info.p_bit_size != NULL?(*p_mem_entry->member_info.p_bit_size): 0;
75+
byte_offset = _data_bit_offset >> 3;
76+
bit_offset = _data_bit_offset & 0x7;
77+
bit_size = _bit_size;
78+
#ifdef PRECHECK_DEBUG
79+
fprintf(stderr, "fld %4s, data_bit_offset %d, bit_size %d => byte_offset %d, bit_offset %d bit_size %d\n",
80+
p_mem_entry->name, (int)(_data_bit_offset), (int)bit_size,
81+
(int)byte_offset, (int)bit_offset, (int)bit_size);
82+
#endif
83+
}
84+
void * base_addr = NULL;
85+
switch (fld_id) {
86+
case 0: // field 'a'
87+
fail_unless(strcmp(p_mem_entry->name, "a") == 0);
88+
precheck_struct.a = 0;
89+
90+
if (IS_LITTLE_ENDIAN) {
91+
base_addr = p_mem_val->addr + byte_offset;
92+
} else {
93+
fail_unless(p_mem_entry->parents_count == 1 && p_mem_entry->parents[0]->structure_type_info.byte_size != 0);
94+
base_addr = p_mem_val->addr + byte_offset;
95+
bit_offset = 8 - (bit_offset + bit_size);
96+
}
97+
do {
98+
#ifdef PRECHECK_DEBUG
99+
fprintf(stderr, "value %d: ", (int)precheck_struct.a);
100+
DUMP_MEM(&precheck_struct, sizeof(precheck_struct));
101+
#endif
102+
fail_unless(base_addr != NULL, "based addr mustn't be NULL");
103+
char data = *((char*)base_addr);
104+
#ifdef PRECHECK_DEBUG
105+
fprintf(stderr, "read %x, bit_offset %x, mask %x => %x\n", (int)data, (int)bit_offset,
106+
(int)((1 << bit_size) - 1),
107+
(int)(data >> bit_offset) & ((1 << bit_size) - 1));
108+
#endif
109+
data = (data >> bit_offset) & ((1 << bit_size) - 1);
110+
if ((enc == METAC_ENC_signed_char) && (data & (1 << (bit_size-1)))) {
111+
data = ((0xff) << ((bit_size))) ^ data;
112+
}
113+
char exp_data = precheck_struct.a;
114+
fail_unless(exp_data == data, "expected %x, got %x", exp_data, data);
115+
++precheck_struct.a;
116+
}while(precheck_struct.a != 0);
117+
break;
118+
case 2: // field 'c'
119+
fail_unless(strcmp(p_mem_entry->name, "c") == 0);
120+
precheck_struct.c = 0;
121+
if (IS_LITTLE_ENDIAN) {
122+
base_addr = p_mem_val->addr + byte_offset;
123+
} else {
124+
fail_unless(p_mem_entry->parents_count == 1 && p_mem_entry->parents[0]->structure_type_info.byte_size != 0);
125+
base_addr = p_mem_val->addr + byte_offset;
126+
bit_offset = 8 - (bit_offset + bit_size);
127+
}
128+
do {
129+
#ifdef PRECHECK_DEBUG
130+
fprintf(stderr, "value %d: ", (int)precheck_struct.c);
131+
DUMP_MEM(&precheck_struct, sizeof(precheck_struct));
132+
#endif
133+
fail_unless(base_addr != NULL, "based addr mustn't be NULL");
134+
char data = *((char*)base_addr);
135+
#ifdef PRECHECK_DEBUG
136+
fprintf(stderr, "read %x, bit_offset %x, mask %x => %x\n", (int)data, (int)bit_offset,
137+
(int)((1 << bit_size) - 1),
138+
(int)(data >> bit_offset) & ((1 << bit_size) - 1));
139+
#endif
140+
data = (data >> bit_offset) & ((1 << bit_size) - 1);
141+
if ((enc == METAC_ENC_signed_char) && (data & (1 << (bit_size-1)))) {
142+
data = ((0xff) << ((bit_size))) ^ data;
143+
}
144+
char exp_data = precheck_struct.c;
145+
fail_unless(exp_data == data, "expected %x, got %x", exp_data, data);
146+
++precheck_struct.c;
147+
}while(precheck_struct.c != 0);
148+
break;
149+
}
150+
metac_value_delete(p_mem_val);
151+
}
152+
metac_value_delete(p_val);
153+
}END_TEST
154+
14155
struct {
15156
char a:3;
16157
unsigned char b:3;

0 commit comments

Comments
 (0)