// SPDX-FileCopyrightText: 2014-2015 pancake // SPDX-License-Identifier: LGPL-3.0-only #include "rz_util/rz_str.h" #include /* dex/dwarf uleb128 implementation */ RZ_API const ut8 *rz_uleb128(const ut8 *data, int datalen, RZ_NULLABLE ut64 *v, const char **error) { ut8 c; ut64 s, sum = 0; const ut8 *data_end; bool malformed_uleb = true; if (v) { *v = 0LL; } if (datalen == ST32_MAX) { // WARNING; possible overflow datalen = 0xffff; } if (datalen < 0) { return NULL; } data_end = data + datalen; if (data && datalen > 0) { if (*data) { for (s = 0; data < data_end; s += 7) { c = *(data++) & 0xff; if (s > 63) { if (error) { *error = rz_str_newf("rz_uleb128: undefined behaviour in %d shift on ut32\n", (int)s); } break; } else { sum |= ((ut64)(c & 0x7f) << s); } if (!(c & 0x80)) { malformed_uleb = false; break; } } if (malformed_uleb) { if (error) { *error = rz_str_newf("malformed uleb128\n"); } } } else { data++; } } if (v) { *v = sum; } return data; } RZ_API int rz_uleb128_len(const ut8 *data, int size) { int i = 1; ut8 c = *(data++); while (c > 0x7f && i < size) { c = *(data++); i++; } return i; } /* data is the char array containing the uleb number * datalen will point (if not NULL) to the length of the uleb number * v (if not NULL) will point to the data's value (if fitting the size of an ut64) */ RZ_API const ut8 *rz_uleb128_decode(const ut8 *data, int *datalen, ut64 *v) { ut8 c = 0xff; ut64 s = 0, sum = 0, l = 0; do { c = *(data++) & 0xff; if (s > 63) { break; } sum |= ((ut64)(c & 0x7f) << s); s += 7; l++; } while (c & 0x80); if (v) { *v = sum; } if (datalen) { *datalen = l; } return data; } RZ_API ut8 *rz_uleb128_encode(const ut64 s, int *len) { ut8 c = 0; int l = 0; ut8 *otarget = NULL, *target = NULL, *tmptarget = NULL; ut64 source = s; do { l++; if (!(tmptarget = realloc(otarget, l))) { l = 0; free(otarget); otarget = NULL; break; } otarget = tmptarget; target = otarget + l - 1; c = source & 0x7f; source >>= 7; if (source) { c |= 0x80; } *(target) = c; } while (source); if (len) { *len = l; } return otarget; } RZ_API const ut8 *rz_leb128(const ut8 *data, int datalen, st64 *v) { ut8 c = 0; st64 s = 0, sum = 0; const ut8 *data_end = data + datalen; if (data && datalen > 0) { if (!*data) { data++; goto beach; } while (data < data_end) { c = *(data++) & 0x0ff; if (s > 63) { break; } sum |= ((st64)(c & 0x7f) << s); s += 7; if (!(c & 0x80)) { break; } } } if ((s < (8 * sizeof(sum))) && (c & 0x40)) { sum |= -((st64)1 << s); } beach: if (v) { *v = sum; } return data; } RZ_API st64 rz_sleb128(const ut8 **data, const ut8 *end) { const ut8 *p = *data; st64 result = 0; int offset = 0; ut8 value; bool cond; do { st64 chunk; value = *p; chunk = value & 0x7f; if (offset > 63) { // overflow of st64 break; } result |= (chunk << offset); offset += 7; } while (cond = *p & 0x80 && p + 1 < end, p++, cond); if ((value & 0x40) != 0) { result |= ~0ULL << offset; } *data = p; return result; } // API from https://github.com/WebAssembly/wabt/blob/master/src/binary-reader.cc #define BYTE_AT(type, i, shift) (((type)(p[i]) & 0x7f) << (shift)) #define LEB128_1(type) (BYTE_AT(type, 0, 0)) #define LEB128_2(type) (BYTE_AT(type, 1, 7) | LEB128_1(type)) #define LEB128_3(type) (BYTE_AT(type, 2, 14) | LEB128_2(type)) #define LEB128_4(type) (BYTE_AT(type, 3, 21) | LEB128_3(type)) #define LEB128_5(type) (BYTE_AT(type, 4, 28) | LEB128_4(type)) #define LEB128_6(type) (BYTE_AT(type, 5, 35) | LEB128_5(type)) #define LEB128_7(type) (BYTE_AT(type, 6, 42) | LEB128_6(type)) #define LEB128_8(type) (BYTE_AT(type, 7, 49) | LEB128_7(type)) #define LEB128_9(type) (BYTE_AT(type, 8, 56) | LEB128_8(type)) #define LEB128_10(type) (BYTE_AT(type, 9, 63) | LEB128_9(type)) #define SHIFT_AMOUNT(type, sign_bit) (sizeof(type) * 8 - 1 - (sign_bit)) #define SIGN_EXTEND(type, value, sign_bit) \ ((type)((value) << SHIFT_AMOUNT(type, sign_bit)) >> \ SHIFT_AMOUNT(type, sign_bit)) RZ_API size_t read_u32_leb128(const ut8 *p, const ut8 *max, ut32 *out_value) { if (p < max && !(p[0] & 0x80)) { *out_value = LEB128_1(ut32); return 1; } else if (p + 1 < max && !(p[1] & 0x80)) { *out_value = LEB128_2(ut32); return 2; } else if (p + 2 < max && !(p[2] & 0x80)) { *out_value = LEB128_3(ut32); return 3; } else if (p + 3 < max && !(p[3] & 0x80)) { *out_value = LEB128_4(ut32); return 4; } else if (p + 4 < max && !(p[4] & 0x80)) { /* the top bits set represent values > 32 bits */ // if (p[4] & 0xf0) {} *out_value = LEB128_5(ut32); return 5; } else { /* past the end */ *out_value = 0; return 0; } } RZ_API size_t read_i32_leb128(const ut8 *p, const ut8 *max, st32 *out_value) { if (p < max && !(p[0] & 0x80)) { ut32 result = LEB128_1(ut32); *out_value = SIGN_EXTEND(st32, result, 6); return 1; } else if (p + 1 < max && !(p[1] & 0x80)) { ut32 result = LEB128_2(ut32); *out_value = SIGN_EXTEND(st32, result, 13); return 2; } else if (p + 2 < max && !(p[2] & 0x80)) { ut32 result = LEB128_3(ut32); *out_value = SIGN_EXTEND(st32, result, 20); return 3; } else if (p + 3 < max && !(p[3] & 0x80)) { ut32 result = LEB128_4(ut32); *out_value = SIGN_EXTEND(st32, result, 27); return 4; } else if (p + 4 < max && !(p[4] & 0x80)) { /* the top bits should be a sign-extension of the sign bit */ bool sign_bit_set = (p[4] & 0x8); int top_bits = p[4] & 0xf0; if ((sign_bit_set && top_bits != 0x70) || (!sign_bit_set && top_bits != 0)) { return 0; } ut32 result = LEB128_5(ut32); *out_value = result; return 5; } else { /* past the end */ return 0; } } RZ_API size_t read_u64_leb128(const ut8 *p, const ut8 *max, ut64 *out_value) { if (p < max && !(p[0] & 0x80)) { *out_value = LEB128_1(ut64); return 1; } else if (p + 1 < max && !(p[1] & 0x80)) { *out_value = LEB128_2(ut64); return 2; } else if (p + 2 < max && !(p[2] & 0x80)) { *out_value = LEB128_3(ut64); return 3; } else if (p + 3 < max && !(p[3] & 0x80)) { *out_value = LEB128_4(ut64); return 4; } else if (p + 4 < max && !(p[4] & 0x80)) { *out_value = LEB128_5(ut64); return 5; } else if (p + 5 < max && !(p[5] & 0x80)) { *out_value = LEB128_6(ut64); return 6; } else if (p + 6 < max && !(p[6] & 0x80)) { *out_value = LEB128_7(ut64); return 7; } else if (p + 7 < max && !(p[7] & 0x80)) { *out_value = LEB128_8(ut64); return 8; } else if (p + 8 < max && !(p[8] & 0x80)) { *out_value = LEB128_9(ut64); return 9; } else if (p + 9 < max && !(p[9] & 0x80)) { *out_value = LEB128_10(ut64); return 10; } else { /* past the end */ *out_value = 0; return 0; } } RZ_API size_t read_i64_leb128(const ut8 *p, const ut8 *max, st64 *out_value) { if (p < max && !(p[0] & 0x80)) { ut64 result = LEB128_1(ut64); *out_value = SIGN_EXTEND(st64, result, 6); return 1; } else if (p + 1 < max && !(p[1] & 0x80)) { ut64 result = LEB128_2(ut64); *out_value = SIGN_EXTEND(st64, result, 13); return 2; } else if (p + 2 < max && !(p[2] & 0x80)) { ut64 result = LEB128_3(ut64); *out_value = SIGN_EXTEND(st64, result, 20); return 3; } else if (p + 3 < max && !(p[3] & 0x80)) { ut64 result = LEB128_4(ut64); *out_value = SIGN_EXTEND(st64, result, 27); return 4; } else if (p + 4 < max && !(p[4] & 0x80)) { ut64 result = LEB128_5(ut64); *out_value = SIGN_EXTEND(st64, result, 34); return 5; } else if (p + 5 < max && !(p[5] & 0x80)) { ut64 result = LEB128_6(ut64); *out_value = SIGN_EXTEND(st64, result, 41); return 6; } else if (p + 6 < max && !(p[6] & 0x80)) { ut64 result = LEB128_7(ut64); *out_value = SIGN_EXTEND(st64, result, 48); return 7; } else if (p + 7 < max && !(p[7] & 0x80)) { ut64 result = LEB128_8(ut64); *out_value = SIGN_EXTEND(st64, result, 55); return 8; } else if (p + 8 < max && !(p[8] & 0x80)) { ut64 result = LEB128_9(ut64); *out_value = SIGN_EXTEND(st64, result, 62); return 9; } else if (p + 9 < max && !(p[9] & 0x80)) { /* the top bits should be a sign-extension of the sign bit */ bool sign_bit_set = (p[9] & 0x1); int top_bits = p[9] & 0xfe; if ((sign_bit_set && top_bits != 0x7e) || (!sign_bit_set && top_bits != 0)) { return 0; } ut64 result = LEB128_10(ut64); *out_value = result; return 10; } else { /* past the end */ return 0; } } #undef BYTE_AT #undef LEB128_1 #undef LEB128_2 #undef LEB128_3 #undef LEB128_4 #undef LEB128_5 #undef LEB128_6 #undef LEB128_7 #undef LEB128_8 #undef LEB128_9 #undef LEB128_10 #undef SHIFT_AMOUNT #undef SIGN_EXTEND