diff --git a/librz/bin/format/mach0/mach0_defines.h b/librz/bin/format/mach0/mach0_defines.h index d23da1f8ad..5d2ed2bc73 100644 --- a/librz/bin/format/mach0/mach0_defines.h +++ b/librz/bin/format/mach0/mach0_defines.h @@ -1483,6 +1483,14 @@ enum { DYLD_CHAINED_PTR_ARM64E_USERLAND24 = 12, }; +#include + +#define READ_BITS(dst, count) \ + do { \ + dst = raw_val & rz_num_bitmask(count); \ + raw_val >>= count; \ + } while (0); + struct dyld_chained_ptr_arm64e_rebase { uint64_t target : 43, high8 : 8, @@ -1491,6 +1499,14 @@ struct dyld_chained_ptr_arm64e_rebase { auth : 1; // == 0 }; +static inline void dyld_chained_ptr_arm64e_rebase_read(struct dyld_chained_ptr_arm64e_rebase *dst, ut64 raw_val) { + READ_BITS(dst->target, 43); + READ_BITS(dst->high8, 8); + READ_BITS(dst->next, 11); + READ_BITS(dst->bind, 1); + READ_BITS(dst->auth, 1); +} + struct dyld_chained_ptr_arm64e_bind { uint64_t ordinal : 16, zero : 16, @@ -1500,6 +1516,15 @@ struct dyld_chained_ptr_arm64e_bind { auth : 1; // == 0 }; +static inline void dyld_chained_ptr_arm64e_bind_read(struct dyld_chained_ptr_arm64e_bind *dst, ut64 raw_val) { + READ_BITS(dst->ordinal, 16); + READ_BITS(dst->zero, 16); + READ_BITS(dst->addend, 19); + READ_BITS(dst->next, 11); + READ_BITS(dst->bind, 1); + READ_BITS(dst->auth, 1); +} + struct dyld_chained_ptr_arm64e_auth_rebase { uint64_t target : 32, diversity : 16, @@ -1510,6 +1535,16 @@ struct dyld_chained_ptr_arm64e_auth_rebase { auth : 1; // == 1 }; +static inline void dyld_chained_ptr_arm64e_auth_rebase_read(struct dyld_chained_ptr_arm64e_auth_rebase *dst, ut64 raw_val) { + READ_BITS(dst->target, 32); + READ_BITS(dst->diversity, 16); + READ_BITS(dst->addrDiv, 1); + READ_BITS(dst->key, 2); + READ_BITS(dst->next, 11); + READ_BITS(dst->bind, 1); + READ_BITS(dst->auth, 1); +} + struct dyld_chained_ptr_arm64e_auth_bind { uint64_t ordinal : 16, zero : 16, @@ -1521,6 +1556,17 @@ struct dyld_chained_ptr_arm64e_auth_bind { auth : 1; // == 1 }; +static inline void dyld_chained_ptr_arm64e_auth_bind_read(struct dyld_chained_ptr_arm64e_auth_bind *dst, ut64 raw_val) { + READ_BITS(dst->ordinal, 16); + READ_BITS(dst->zero, 16); + READ_BITS(dst->diversity, 16); + READ_BITS(dst->addrDiv, 1); + READ_BITS(dst->key, 2); + READ_BITS(dst->next, 11); + READ_BITS(dst->bind, 1); + READ_BITS(dst->auth, 1); +} + struct dyld_chained_ptr_64_rebase { uint64_t target : 36, high8 : 8, @@ -1529,6 +1575,14 @@ struct dyld_chained_ptr_64_rebase { bind : 1; // == 0 }; +static inline void dyld_chained_ptr_64_rebase_read(struct dyld_chained_ptr_64_rebase *dst, ut64 raw_val) { + READ_BITS(dst->target, 36); + READ_BITS(dst->high8, 8); + READ_BITS(dst->reserved, 7); + READ_BITS(dst->next, 12); + READ_BITS(dst->bind, 1); +} + struct dyld_chained_ptr_64_bind { uint64_t ordinal : 24, addend : 8, @@ -1537,6 +1591,14 @@ struct dyld_chained_ptr_64_bind { bind : 1; // == 1 }; +static inline void dyld_chained_ptr_64_bind_read(struct dyld_chained_ptr_64_bind *dst, ut64 raw_val) { + READ_BITS(dst->ordinal, 24); + READ_BITS(dst->addend, 8); + READ_BITS(dst->reserved, 19); + READ_BITS(dst->next, 12); + READ_BITS(dst->bind, 1); +} + /* WARNING: this is guesswork based on trial and error */ struct dyld_chained_ptr_arm64e_cache_rebase { uint64_t target : 43, @@ -1545,6 +1607,13 @@ struct dyld_chained_ptr_arm64e_cache_rebase { auth : 1; // == 0 }; +static inline void dyld_chained_ptr_arm64e_cache_rebase_read(struct dyld_chained_ptr_arm64e_cache_rebase *dst, ut64 raw_val) { + READ_BITS(dst->target, 43); + READ_BITS(dst->high8, 8); + READ_BITS(dst->next, 12); + READ_BITS(dst->auth, 1); +} + struct dyld_chained_ptr_arm64e_cache_auth_rebase { uint64_t target : 32, diversity : 16, @@ -1554,6 +1623,15 @@ struct dyld_chained_ptr_arm64e_cache_auth_rebase { auth : 1; // == 1 }; +static inline void dyld_chained_ptr_arm64e_cache_auth_rebase_read(struct dyld_chained_ptr_arm64e_cache_auth_rebase *dst, ut64 raw_val) { + READ_BITS(dst->target, 32); + READ_BITS(dst->diversity, 16); + READ_BITS(dst->addrDiv, 1); + READ_BITS(dst->key, 2); + READ_BITS(dst->next, 12); + READ_BITS(dst->auth, 1); +} + struct dyld_chained_ptr_arm64e_bind24 { uint64_t ordinal : 24, zero : 8, @@ -1563,6 +1641,15 @@ struct dyld_chained_ptr_arm64e_bind24 { auth : 1; // == 0 }; +static inline void dyld_chained_ptr_arm64e_bind24_read(struct dyld_chained_ptr_arm64e_bind24 *dst, ut64 raw_val) { + READ_BITS(dst->ordinal, 24); + READ_BITS(dst->zero, 8); + READ_BITS(dst->addend, 19); + READ_BITS(dst->next, 11); + READ_BITS(dst->bind, 1); + READ_BITS(dst->auth, 1); +} + struct dyld_chained_ptr_arm64e_auth_bind24 { uint64_t ordinal : 24, zero : 8, @@ -1574,4 +1661,19 @@ struct dyld_chained_ptr_arm64e_auth_bind24 { auth : 1; // == 1 }; +static inline void dyld_chained_ptr_arm64e_auth_bind24_read(struct dyld_chained_ptr_arm64e_auth_bind24 *dst, ut64 raw_val) { + READ_BITS(dst->ordinal, 24); + READ_BITS(dst->zero, 8); + READ_BITS(dst->diversity, 16); + READ_BITS(dst->addrDiv, 1); + READ_BITS(dst->key, 2); + READ_BITS(dst->next, 11); + READ_BITS(dst->bind, 1); + READ_BITS(dst->auth, 1); +} + +// When adding more structs/readers here, also add tests to test/unit/test_bin_mach0.c! + +#undef READ_BITS + #endif diff --git a/librz/bin/format/mach0/mach0_rebase.c b/librz/bin/format/mach0/mach0_rebase.c index 9a233f2309..91733985d0 100644 --- a/librz/bin/format/mach0/mach0_rebase.c +++ b/librz/bin/format/mach0/mach0_rebase.c @@ -61,23 +61,23 @@ RZ_API void MACH0_(rebase_buffer)(struct MACH0_(obj_t) * obj, ut64 off, ut8 *buf case DYLD_CHAINED_PTR_ARM64E: { bool is_bind = IS_PTR_BIND(raw_ptr); if (is_auth && is_bind) { - struct dyld_chained_ptr_arm64e_auth_bind *p = - (struct dyld_chained_ptr_arm64e_auth_bind *)&raw_ptr; - delta = p->next; + struct dyld_chained_ptr_arm64e_auth_bind p; + dyld_chained_ptr_arm64e_auth_bind_read(&p, raw_ptr); + delta = p.next; } else if (!is_auth && is_bind) { - struct dyld_chained_ptr_arm64e_bind *p = - (struct dyld_chained_ptr_arm64e_bind *)&raw_ptr; - delta = p->next; + struct dyld_chained_ptr_arm64e_bind p; + dyld_chained_ptr_arm64e_bind_read(&p, raw_ptr); + delta = p.next; } else if (is_auth && !is_bind) { - struct dyld_chained_ptr_arm64e_auth_rebase *p = - (struct dyld_chained_ptr_arm64e_auth_rebase *)&raw_ptr; - delta = p->next; - ptr_value = p->target + obj->baddr; + struct dyld_chained_ptr_arm64e_auth_rebase p; + dyld_chained_ptr_arm64e_auth_rebase_read(&p, raw_ptr); + delta = p.next; + ptr_value = p.target + obj->baddr; } else { - struct dyld_chained_ptr_arm64e_rebase *p = - (struct dyld_chained_ptr_arm64e_rebase *)&raw_ptr; - delta = p->next; - ptr_value = ((ut64)p->high8 << 56) | p->target; + struct dyld_chained_ptr_arm64e_rebase p; + dyld_chained_ptr_arm64e_rebase_read(&p, raw_ptr); + delta = p.next; + ptr_value = ((ut64)p.high8 << 56) | p.target; } break; } @@ -85,15 +85,15 @@ RZ_API void MACH0_(rebase_buffer)(struct MACH0_(obj_t) * obj, ut64 off, ut8 *buf case DYLD_CHAINED_PTR_ARM64E_KERNEL: { stride = 4; if (is_auth) { - struct dyld_chained_ptr_arm64e_cache_auth_rebase *p = - (struct dyld_chained_ptr_arm64e_cache_auth_rebase *)&raw_ptr; - delta = p->next; - ptr_value = p->target + obj->baddr; + struct dyld_chained_ptr_arm64e_cache_auth_rebase p; + dyld_chained_ptr_arm64e_cache_auth_rebase_read(&p, raw_ptr); + delta = p.next; + ptr_value = p.target + obj->baddr; } else { - struct dyld_chained_ptr_arm64e_cache_rebase *p = - (struct dyld_chained_ptr_arm64e_cache_rebase *)&raw_ptr; - delta = p->next; - ptr_value = ((ut64)p->high8 << 56) | p->target; + struct dyld_chained_ptr_arm64e_cache_rebase p; + dyld_chained_ptr_arm64e_cache_rebase_read(&p, raw_ptr); + delta = p.next; + ptr_value = ((ut64)p.high8 << 56) | p.target; ptr_value += obj->baddr; } break; @@ -101,15 +101,15 @@ RZ_API void MACH0_(rebase_buffer)(struct MACH0_(obj_t) * obj, ut64 off, ut8 *buf case DYLD_CHAINED_PTR_64: case DYLD_CHAINED_PTR_64_OFFSET: { stride = 4; - struct dyld_chained_ptr_64_bind *bind = - (struct dyld_chained_ptr_64_bind *)&raw_ptr; - if (bind->bind) { - delta = bind->next; + struct dyld_chained_ptr_64_bind bind; + dyld_chained_ptr_64_bind_read(&bind, raw_ptr); + if (bind.bind) { + delta = bind.next; } else { - struct dyld_chained_ptr_64_rebase *p = - (struct dyld_chained_ptr_64_rebase *)&raw_ptr; - delta = p->next; - ptr_value = (((ut64)p->high8 << 56) | p->target); + struct dyld_chained_ptr_64_rebase p; + dyld_chained_ptr_64_rebase_read(&p, raw_ptr); + delta = p.next; + ptr_value = (((ut64)p.high8 << 56) | p.target); if (segment->pointer_format == DYLD_CHAINED_PTR_64_OFFSET) { ptr_value += obj->baddr; } @@ -118,21 +118,21 @@ RZ_API void MACH0_(rebase_buffer)(struct MACH0_(obj_t) * obj, ut64 off, ut8 *buf } case DYLD_CHAINED_PTR_ARM64E_USERLAND24: { stride = 8; - struct dyld_chained_ptr_arm64e_bind24 *bind = - (struct dyld_chained_ptr_arm64e_bind24 *)&raw_ptr; - if (bind->bind) { - delta = bind->next; + struct dyld_chained_ptr_arm64e_bind24 bind; + dyld_chained_ptr_arm64e_bind24_read(&bind, raw_ptr); + if (bind.bind) { + delta = bind.next; } else { - if (bind->auth) { - struct dyld_chained_ptr_arm64e_auth_rebase *p = - (struct dyld_chained_ptr_arm64e_auth_rebase *)&raw_ptr; - delta = p->next; - ptr_value = p->target + obj->baddr; + if (bind.auth) { + struct dyld_chained_ptr_arm64e_auth_rebase p; + dyld_chained_ptr_arm64e_auth_rebase_read(&p, raw_ptr); + delta = p.next; + ptr_value = p.target + obj->baddr; } else { - struct dyld_chained_ptr_arm64e_rebase *p = - (struct dyld_chained_ptr_arm64e_rebase *)&raw_ptr; - delta = p->next; - ptr_value = obj->baddr + (((ut64)p->high8 << 56) | p->target); + struct dyld_chained_ptr_arm64e_rebase p; + dyld_chained_ptr_arm64e_rebase_read(&p, raw_ptr); + delta = p.next; + ptr_value = obj->baddr + (((ut64)p.high8 << 56) | p.target); } } break; diff --git a/librz/bin/format/mach0/mach0_relocs.c b/librz/bin/format/mach0/mach0_relocs.c index 4a7ebd1334..b67ab8c57f 100644 --- a/librz/bin/format/mach0/mach0_relocs.c +++ b/librz/bin/format/mach0/mach0_relocs.c @@ -254,24 +254,24 @@ RZ_BORROW RzSkipList *MACH0_(get_relocs)(struct MACH0_(obj_t) * bin) { int addend = -1; ut64 delta; if (is_auth && is_bind) { - struct dyld_chained_ptr_arm64e_auth_bind *p = - (struct dyld_chained_ptr_arm64e_auth_bind *)&raw_ptr; - delta = p->next; - ordinal = p->ordinal; + struct dyld_chained_ptr_arm64e_auth_bind p; + dyld_chained_ptr_arm64e_auth_bind_read(&p, raw_ptr); + delta = p.next; + ordinal = p.ordinal; } else if (!is_auth && is_bind) { - struct dyld_chained_ptr_arm64e_bind *p = - (struct dyld_chained_ptr_arm64e_bind *)&raw_ptr; - delta = p->next; - ordinal = p->ordinal; - addend = p->addend; + struct dyld_chained_ptr_arm64e_bind p; + dyld_chained_ptr_arm64e_bind_read(&p, raw_ptr); + delta = p.next; + ordinal = p.ordinal; + addend = p.addend; } else if (is_auth && !is_bind) { - struct dyld_chained_ptr_arm64e_auth_rebase *p = - (struct dyld_chained_ptr_arm64e_auth_rebase *)&raw_ptr; - delta = p->next; + struct dyld_chained_ptr_arm64e_auth_rebase p; + dyld_chained_ptr_arm64e_auth_rebase_read(&p, raw_ptr); + delta = p.next; } else { - struct dyld_chained_ptr_arm64e_rebase *p = - (struct dyld_chained_ptr_arm64e_rebase *)&raw_ptr; - delta = p->next; + struct dyld_chained_ptr_arm64e_rebase p; + dyld_chained_ptr_arm64e_rebase_read(&p, raw_ptr); + delta = p.next; } if (ordinal != -1) { if (ordinal >= n_threaded_binds) { diff --git a/test/unit/meson.build b/test/unit/meson.build index 7b0eb72e09..ec1f864e1c 100644 --- a/test/unit/meson.build +++ b/test/unit/meson.build @@ -25,6 +25,7 @@ if get_option('enable_tests') 'base64', 'big', 'bin_lines', + 'bin_mach0', 'bitmap', 'bitvector', 'buf', diff --git a/test/unit/test_bin_mach0.c b/test/unit/test_bin_mach0.c new file mode 100644 index 0000000000..8f3d385671 --- /dev/null +++ b/test/unit/test_bin_mach0.c @@ -0,0 +1,84 @@ +// SPDX-FileCopyrightText: 2022 Florian Märkl +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include "../../librz/bin/format/mach0/mach0_defines.h" +#include "minunit.h" + +/* + * In real dyld, the dyld_chained_ptr_* structs are parsed by simply + * reinterpreting a 64bit value as the respective bitfield struct. This of + * course only works on little endian hosts, but we want to be + * endian-independent, so we define or own readers. + * The tests below compare the output of our readers against the dyld-style + * reinterpretation to make sure we are parsing correctly. For adding tests for + * a new struct, simply add a TEST_READ_DEF(...) and + * mu_run_test(test_dyld_chained_..._read); line. + */ + +#if !RZ_SYS_ENDIAN + +#define SAMPLES 10000 + +static ut64 rand_ut64() { + ut64 r = 0; + for (int i = 0; i < 8; i++) { + r = (rand() % 0xff) | (r << 8); + } + return r; +} + +#define TEST_READ_DEF(name) \ + bool test_##name##_read() { \ + RZ_STATIC_ASSERT(sizeof(struct name) == sizeof(ut64)); \ + for (int i = 0; i < SAMPLES; i++) { \ + ut64 raw_val = rand_ut64(); /* this is the value we want to parse */ \ +\ + /* Parse with our endian-independent function */ \ + struct name s; \ + ut64 *s_direct = (ut64 *)&s; \ + *s_direct = rand_ut64(); /* init with garbage */ \ + name##_read(&s, raw_val); \ +\ + /* Compare our parsed value against a direct copy into the struct. */ \ + /* This only works on little endian hosts! */ \ + if (*s_direct != raw_val) { /* manual check to avoid rz_strf below */ \ + char message[128]; \ + snprintf(message, sizeof(message), #name "_read(0x%" PFMT64x ")", raw_val); \ + mu_assert_eq(*s_direct, raw_val, message); \ + } \ + } \ + mu_end; \ + } + +TEST_READ_DEF(dyld_chained_ptr_arm64e_rebase) +TEST_READ_DEF(dyld_chained_ptr_arm64e_bind) +TEST_READ_DEF(dyld_chained_ptr_arm64e_auth_rebase) +TEST_READ_DEF(dyld_chained_ptr_arm64e_auth_bind) +TEST_READ_DEF(dyld_chained_ptr_64_rebase) +TEST_READ_DEF(dyld_chained_ptr_64_bind) +TEST_READ_DEF(dyld_chained_ptr_arm64e_cache_rebase) +TEST_READ_DEF(dyld_chained_ptr_arm64e_cache_auth_rebase) +TEST_READ_DEF(dyld_chained_ptr_arm64e_bind24) +TEST_READ_DEF(dyld_chained_ptr_arm64e_auth_bind24) + +#endif + +bool all_tests() { + srand(time(0)); +#if !RZ_SYS_ENDIAN + mu_run_test(test_dyld_chained_ptr_arm64e_rebase_read); + mu_run_test(test_dyld_chained_ptr_arm64e_bind_read); + mu_run_test(test_dyld_chained_ptr_arm64e_auth_rebase_read); + mu_run_test(test_dyld_chained_ptr_arm64e_auth_bind_read); + mu_run_test(test_dyld_chained_ptr_64_rebase_read); + mu_run_test(test_dyld_chained_ptr_64_bind_read); + mu_run_test(test_dyld_chained_ptr_arm64e_cache_rebase_read); + mu_run_test(test_dyld_chained_ptr_arm64e_cache_auth_rebase_read); + mu_run_test(test_dyld_chained_ptr_arm64e_bind24_read); + mu_run_test(test_dyld_chained_ptr_arm64e_auth_bind24_read); +#endif + return tests_passed != tests_run; +} + +mu_main(all_tests)