Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Big endian build to test bitfields #20

Merged
merged 8 commits into from
Aug 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 43 additions & 2 deletions .github/workflows/buildAndTest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,47 @@ jobs:
- name: Examples test
run: make test all DWARF_VER_FORCE=${{matrix.dwarf-version}}

ubuntu_big_endian:
# pretty slow since it's running on qemu. we may disable it periodically
# taken from https://github.com/marketplace/actions/run-on-architecture
# s390x is big endian with Ubuntu_latest which
# also has go-lang for linux: https://go.dev/dl/
# https://go.dev/dl/go1.23.0.linux-s390x.tar.gz
# GO_TELEMETRY_CHILD=off
# The host should always be Linux.
runs-on: ubuntu-22.04
name: ubuntu (s390x - big endian)
steps:
- uses: actions/checkout@v4
- name: Download Go
run: curl -fsSL -o go.tar.gz https://go.dev/dl/go1.22.0.linux-s390x.tar.gz
- uses: uraimo/run-on-arch-action@v2
name: Run commands
id: runcmd
with:
arch: s390x
distro: ubuntu_latest

# Not required, but speeds up builds by storing container images in
# a GitHub package registry.
githubToken: ${{ github.token }}

# Set an output parameter `uname` for use in subsequent steps
run: |
set -x
uname -a
apt update && apt-get install -y ca-certificates pkg-config make gcc g++ check # todo: libffi-dev

tar -C /usr/local -xzf go.tar.gz && rm go.tar.gz
export PATH=$PATH:/usr/local/go/bin
go version

export GO111MODULE=on
go test -cover ./...
go build

make test all

macos:
runs-on: macos-latest
strategy:
Expand Down Expand Up @@ -70,7 +111,7 @@ jobs:
msystem: ${{matrix.sys}}
update: true
install: base-devel git pkg-config mingw-w64-${{matrix.env}}-check mingw-w64-${{matrix.env}}-toolchain
path-type: inherit # to be able to find go
path-type: inherit # to be able to find go in PATH
- name: Checkout code
uses: actions/checkout@v3
- name: Test
Expand All @@ -80,4 +121,4 @@ jobs:
- name: Build
run: go build
- name: Examples test
run: make test all
run: make test all
3 changes: 1 addition & 2 deletions src/entry.c
Original file line number Diff line number Diff line change
Expand Up @@ -443,11 +443,10 @@ int metac_entry_member_bitfield_offsets(metac_entry_t *p_memb_entry,
*p_bit_offset = _data_bit_offset & 0x7;
} else if (p_memb_entry->member_info.p_data_bit_offset != NULL) { /* dwarf 4 */
// in data

metac_offset_t _data_bit_offset = *p_memb_entry->member_info.p_data_bit_offset;
*p_bit_size = p_memb_entry->member_info.p_bit_size != NULL?(*p_memb_entry->member_info.p_bit_size): 0;
// out

// out
*p_byte_offset = _data_bit_offset >> 3;
*p_bit_offset = _data_bit_offset & 0x7;
}
Expand Down
22 changes: 6 additions & 16 deletions src/value.c
Original file line number Diff line number Diff line change
Expand Up @@ -133,16 +133,11 @@ static int _entry_bitfield_read(metac_entry_t *p_memb_entry, void * base_addr, v
// TODO: make it without extra copy if possible
// TODO: need to make it for both endians
assert(bit_size != 0);
memcpy(buf, base_addr, (bit_offset + bit_size-1)/8 +1);
memcpy(buf, base_addr, (bit_offset + bit_size - 1)/8 + 1);
base_addr = buf;
} else {
assert(metac_entry_parent_count(p_memb_entry) == 1);
metac_entry_t * p_memb_parent = metac_entry_parent_entry(p_memb_entry, 0);
assert(p_memb_parent != NULL);
metac_size_t parent_byte_size = 0;
_check_(metac_entry_byte_size(p_memb_parent, &parent_byte_size) != 0, -(EFAULT));
assert(parent_byte_size > 0);
base_addr += parent_byte_size - byte_offset;
base_addr += byte_offset;
bit_offset = 8 * var_size - (bit_offset + bit_size);
}

#define _read_(_type_) \
Expand Down Expand Up @@ -194,16 +189,11 @@ static int _entry_bitfield_write(metac_entry_t *p_memb_entry, void * base_addr,
// TODO: need to make it for both endians
assert(bit_size != 0);
base_addr_orig = base_addr;
memcpy(buf, base_addr, (bit_offset + bit_size-1)/8 +1);
memcpy(buf, base_addr, (bit_offset + bit_size - 1)/8 + 1);
base_addr = buf;
} else {
assert(metac_entry_parent_count(p_memb_entry) == 1);
metac_entry_t * p_memb_parent = metac_entry_parent_entry(p_memb_entry, 0);
assert(p_memb_parent != NULL);
metac_size_t parent_byte_size = 0;
_check_(metac_entry_byte_size(p_memb_parent, &parent_byte_size) != 0, -(EFAULT));
assert(parent_byte_size > 0);
base_addr += parent_byte_size - byte_offset;
base_addr += byte_offset;
bit_offset = 8 * var_size - (bit_offset + bit_size);
}

#define _write_(_type_) \
Expand Down
9 changes: 6 additions & 3 deletions src/value_base_type.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ dsprintf_render_with_buf(64)
#endif

metac_flag_t metac_entry_is_char(metac_entry_t * p_entry) {
return metac_entry_check_base_type(p_entry, "char", METAC_ENC_signed_char, sizeof(char)) == 0;
return metac_entry_check_base_type(p_entry, "char", METAC_ENC_signed_char, sizeof(char)) == 0 ||
metac_entry_check_base_type(p_entry, "char", METAC_ENC_unsigned_char, sizeof(char)) == 0; // some platforms are doing this
}
metac_flag_t metac_entry_is_uchar(metac_entry_t * p_entry) {
return metac_entry_check_base_type(p_entry, "unsigned char", METAC_ENC_unsigned_char, sizeof(unsigned char)) == 0;
Expand Down Expand Up @@ -115,7 +116,8 @@ metac_flag_t metac_value_is_ldouble_complex(metac_value_t * p_val) {
}
/**/
int metac_value_char(metac_value_t * p_val, char *p_var) {
return metac_value_base_type(p_val, "char", METAC_ENC_signed_char, (void*)p_var, sizeof(*p_var));
return (metac_value_base_type(p_val, "char", METAC_ENC_signed_char, (void*)p_var, sizeof(*p_var)) == 0 ||
metac_value_base_type(p_val, "char", METAC_ENC_unsigned_char, (void*)p_var, sizeof(*p_var)) == 0)?0:-(EFAULT);
}
int metac_value_uchar(metac_value_t * p_val, unsigned char *p_var) {
return metac_value_base_type(p_val, "unsigned char", METAC_ENC_unsigned_char, (void*)p_var, sizeof(*p_var));
Expand Down Expand Up @@ -167,7 +169,8 @@ int metac_value_ldouble_complex(metac_value_t * p_val, long double complex *p_va
}
/* */
int metac_value_set_char(metac_value_t * p_val, char var) {
return metac_value_set_base_type(p_val, "char", METAC_ENC_signed_char, &var, sizeof(var));
return (metac_value_set_base_type(p_val, "char", METAC_ENC_signed_char, &var, sizeof(var)) == 0 ||
metac_value_set_base_type(p_val, "char", METAC_ENC_unsigned_char, &var, sizeof(var)) == 0)?0:-(EFAULT);
}
int metac_value_set_uchar(metac_value_t * p_val, unsigned char var) {
return metac_value_set_base_type(p_val, "unsigned char", METAC_ENC_unsigned_char, &var, sizeof(var));
Expand Down
13 changes: 13 additions & 0 deletions src/value_base_type_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,19 @@
static void test_char(metac_value_t * p_val, char * p_actual_data, metac_flag_t expected_write_err) {
fail_unless(p_val != NULL, "couldn't create p_val");

metac_entry_t *p_entry = metac_entry_final_entry(metac_value_entry(p_val), NULL);
fail_unless(metac_entry_is_base_type(p_entry)!=0);
fail_unless(metac_entry_name(p_entry) != 0, "name is null");
fail_unless(strcmp("char", metac_entry_name(p_entry)) == 0, "expected char, got %s", metac_entry_name(p_entry));
metac_size_t sz;
fail_unless(metac_entry_byte_size(p_entry, &sz) == 0);
fail_unless(sz == sizeof(char), "sz got %d, expected %d", (int)sz, (int)sizeof(char));
metac_encoding_t enc;
fail_unless(metac_entry_base_type_encoding(p_entry, &enc)==0);
fail_unless(enc == METAC_ENC_signed_char || enc == METAC_ENC_unsigned_char, "sz got %d, expected %d or %d",
(int)enc, (int)METAC_ENC_signed_char, (int)METAC_ENC_unsigned_char);


char target;
fail_unless(metac_value_is_char(p_val) != 0, "0 metac_value_is_char returned error");
fail_unless(metac_value_char(p_val, &target) == 0, "0 metac_value_char returned error");
Expand Down
141 changes: 141 additions & 0 deletions src/value_bitfields_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,147 @@
#include "value_base_type.c"
#include "value_with_args.c"

// uncomment the next line if the test doesn't work
// #define PRECHECK_DEBUG
struct {
char a:2;
char b:3;
char c:3; // goes beyond the first char (shoud be moved to the next byte by compiler)
int x: 10;
long z: 22;
}precheck_struct;
METAC_GSYM_LINK(precheck_struct);

#define DUMP_MEM(_start_, _size_) \
do { \
int i; \
unsigned char * p = (unsigned char *)_start_; \
for (i=0; i<(_size_); i++) { \
fprintf(stderr, "%02x ", (int)*p++); \
} \
fprintf(stderr, "\n"); \
}while(0)

METAC_START_TEST(pre_check) {
metac_value_t * p_val = METAC_VALUE_FROM_LINK(precheck_struct);
char target;
fail_unless(metac_value_char(p_val, &target) != 0, "metac_value_char must return error because p_val isn't char");
metac_num_t count = metac_value_member_count(p_val);
fail_unless(count != 0, "got empty struct");
for (metac_num_t fld_id = 0; fld_id < count; ++fld_id) {
metac_value_t * p_mem_val = metac_new_value_by_member_id(p_val, fld_id);
metac_entry_t * p_mem_entry = p_mem_val->p_entry;

metac_entry_t * p_final_entry = metac_entry_final_entry(p_mem_entry, NULL);
metac_encoding_t enc;
fail_unless(metac_entry_base_type_encoding(p_final_entry, &enc)==0);

fail_unless(p_mem_entry->kind == METAC_KND_member, "must be member info kind");
metac_offset_t byte_offset;
metac_offset_t bit_offset;
metac_offset_t bit_size;
if (p_mem_entry->member_info.p_bit_offset != NULL) {
#ifdef PRECHECK_DEBUG
fprintf(stderr, "DWARF 3, LE %d\n", (int)IS_LITTLE_ENDIAN);
#endif
metac_offset_t _bit_size = (p_mem_entry->member_info.p_bit_size?(*p_mem_entry->member_info.p_bit_size):0),
_bit_offset = (p_mem_entry->member_info.p_bit_offset?(*p_mem_entry->member_info.p_bit_offset):0),
_byte_size = (p_mem_entry->member_info.p_byte_size?(*p_mem_entry->member_info.p_byte_size):0);
metac_offset_t _data_bit_offset = (p_mem_entry->member_info.byte_offset + _byte_size) * 8 - (_bit_offset + _bit_size);
byte_offset = _data_bit_offset >> 3;
bit_offset = _data_bit_offset & 0x7;
bit_size = _bit_size;
#ifdef PRECHECK_DEBUG
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",
p_mem_entry->name, (int)(_bit_offset), (int)_bit_size, (int)p_mem_entry->member_info.byte_offset, (int)_byte_size,
(int)byte_offset, (int)bit_offset, (int)bit_size);
#endif
} else if (p_mem_entry->member_info.p_data_bit_offset != NULL) { // test DWARF 4 approach
#ifdef PRECHECK_DEBUG
fprintf(stderr, "DWARF 4, LE %d\n", (int)IS_LITTLE_ENDIAN);
#endif
metac_offset_t _data_bit_offset = *p_mem_entry->member_info.p_data_bit_offset;
metac_offset_t _bit_size = p_mem_entry->member_info.p_bit_size != NULL?(*p_mem_entry->member_info.p_bit_size): 0;
byte_offset = _data_bit_offset >> 3;
bit_offset = _data_bit_offset & 0x7;
bit_size = _bit_size;
#ifdef PRECHECK_DEBUG
fprintf(stderr, "fld %4s, data_bit_offset %d, bit_size %d => byte_offset %d, bit_offset %d bit_size %d\n",
p_mem_entry->name, (int)(_data_bit_offset), (int)bit_size,
(int)byte_offset, (int)bit_offset, (int)bit_size);
#endif
}
void * base_addr = NULL;
switch (fld_id) {
case 0: // field 'a'
fail_unless(strcmp(p_mem_entry->name, "a") == 0);
precheck_struct.a = 0;

if (IS_LITTLE_ENDIAN) {
base_addr = p_mem_val->addr + byte_offset;
} else {
fail_unless(p_mem_entry->parents_count == 1 && p_mem_entry->parents[0]->structure_type_info.byte_size != 0);
base_addr = p_mem_val->addr + byte_offset;
bit_offset = 8 - (bit_offset + bit_size);
}
do {
#ifdef PRECHECK_DEBUG
fprintf(stderr, "value %d: ", (int)precheck_struct.a);
DUMP_MEM(&precheck_struct, sizeof(precheck_struct));
#endif
fail_unless(base_addr != NULL, "based addr mustn't be NULL");
char data = *((char*)base_addr);
#ifdef PRECHECK_DEBUG
fprintf(stderr, "read %x, bit_offset %x, mask %x => %x\n", (int)data, (int)bit_offset,
(int)((1 << bit_size) - 1),
(int)(data >> bit_offset) & ((1 << bit_size) - 1));
#endif
data = (data >> bit_offset) & ((1 << bit_size) - 1);
if ((enc == METAC_ENC_signed_char) && (data & (1 << (bit_size-1)))) {
data = ((0xff) << ((bit_size))) ^ data;
}
char exp_data = precheck_struct.a;
fail_unless(exp_data == data, "expected %x, got %x", exp_data, data);
++precheck_struct.a;
}while(precheck_struct.a != 0);
break;
case 2: // field 'c'
fail_unless(strcmp(p_mem_entry->name, "c") == 0);
precheck_struct.c = 0;
if (IS_LITTLE_ENDIAN) {
base_addr = p_mem_val->addr + byte_offset;
} else {
fail_unless(p_mem_entry->parents_count == 1 && p_mem_entry->parents[0]->structure_type_info.byte_size != 0);
base_addr = p_mem_val->addr + byte_offset;
bit_offset = 8 - (bit_offset + bit_size);
}
do {
#ifdef PRECHECK_DEBUG
fprintf(stderr, "value %d: ", (int)precheck_struct.c);
DUMP_MEM(&precheck_struct, sizeof(precheck_struct));
#endif
fail_unless(base_addr != NULL, "based addr mustn't be NULL");
char data = *((char*)base_addr);
#ifdef PRECHECK_DEBUG
fprintf(stderr, "read %x, bit_offset %x, mask %x => %x\n", (int)data, (int)bit_offset,
(int)((1 << bit_size) - 1),
(int)(data >> bit_offset) & ((1 << bit_size) - 1));
#endif
data = (data >> bit_offset) & ((1 << bit_size) - 1);
if ((enc == METAC_ENC_signed_char) && (data & (1 << (bit_size-1)))) {
data = ((0xff) << ((bit_size))) ^ data;
}
char exp_data = precheck_struct.c;
fail_unless(exp_data == data, "expected %x, got %x", exp_data, data);
++precheck_struct.c;
}while(precheck_struct.c != 0);
break;
}
metac_value_delete(p_mem_val);
}
metac_value_delete(p_val);
}END_TEST

struct {
char a:3;
unsigned char b:3;
Expand Down
Loading