diff --git a/.github/workflows/buildAndTest.yaml b/.github/workflows/buildAndTest.yaml index 0f414c0..7d2c5ce 100644 --- a/.github/workflows/buildAndTest.yaml +++ b/.github/workflows/buildAndTest.yaml @@ -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: @@ -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 @@ -80,4 +121,4 @@ jobs: - name: Build run: go build - name: Examples test - run: make test all \ No newline at end of file + run: make test all diff --git a/src/entry.c b/src/entry.c index 96d76c5..362a2cb 100644 --- a/src/entry.c +++ b/src/entry.c @@ -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; } diff --git a/src/value.c b/src/value.c index 2abfd8b..a25b846 100644 --- a/src/value.c +++ b/src/value.c @@ -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_) \ @@ -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_) \ diff --git a/src/value_base_type.c b/src/value_base_type.c index d4a8451..7ece656 100644 --- a/src/value_base_type.c +++ b/src/value_base_type.c @@ -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; @@ -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)); @@ -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)); diff --git a/src/value_base_type_test.c b/src/value_base_type_test.c index 73d960f..90b9cbd 100644 --- a/src/value_base_type_test.c +++ b/src/value_base_type_test.c @@ -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"); diff --git a/src/value_bitfields_test.c b/src/value_bitfields_test.c index 73a3846..68a7267 100644 --- a/src/value_bitfields_test.c +++ b/src/value_bitfields_test.c @@ -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;