diff options
author | Brian Smith <brian@dbsoft.org> | 2022-06-15 04:34:44 -0500 |
---|---|---|
committer | Brian Smith <brian@dbsoft.org> | 2022-06-15 04:34:44 -0500 |
commit | 532cd0ae7a726a245380f00476b5474b25d55c7a (patch) | |
tree | 0c86622f8b9edbb27a9584c67447a481ad6d9414 | |
parent | 5e9342a622e25b7179e31324b268a42dca5ff6f5 (diff) | |
download | uxp-532cd0ae7a726a245380f00476b5474b25d55c7a.tar.gz |
Issue #1905 - Part 4d - Fix a crash calling a date and time function.
Fixed by updating the xptcinvoke ARM64 code to the latest Mozilla version.
-rw-r--r-- | xpcom/reflect/xptcall/md/unix/xptcinvoke_aarch64.cpp | 180 |
1 files changed, 104 insertions, 76 deletions
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_aarch64.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_aarch64.cpp index e5807dbcde..385cba17ad 100644 --- a/xpcom/reflect/xptcall/md/unix/xptcinvoke_aarch64.cpp +++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_aarch64.cpp @@ -23,109 +23,137 @@ #error "Only little endian compatibility was tested" #endif -/* - * Allocation of integer function arguments initially to registers r1-r7 - * and then to stack. Handling of 'that' argument which goes to register r0 - * is handled separately and does not belong here. - * - * 'ireg_args' - pointer to the current position in the buffer, - * corresponding to the register arguments - * 'stack_args' - pointer to the current position in the buffer, - * corresponding to the arguments on stack - * 'end' - pointer to the end of the registers argument - * buffer. - */ -static inline void alloc_word(uint64_t* &ireg_args, - uint64_t* &stack_args, - uint64_t* end, - uint64_t data) -{ - if (ireg_args < end) { - *ireg_args = data; - ireg_args++; - } else { - *stack_args = data; - stack_args++; - } +// The AAPCS doesn't require argument widening, but Apple's calling convention +// does. If we are really fortunate, the compiler will clean up all the +// copying for us. +template<typename T> +inline uint64_t normalize_arg(T value) { + return (uint64_t)value; } -static inline void alloc_double(double* &freg_args, - uint64_t* &stack_args, - double* end, - double data) -{ - if (freg_args < end) { - *freg_args = data; - freg_args++; - } else { - memcpy(stack_args, &data, sizeof(data)); - stack_args++; - } +template<> +inline uint64_t normalize_arg(float value) { + uint64_t result = 0; + memcpy(&result, &value, sizeof(value)); + return result; +} + +template<> +inline uint64_t normalize_arg(double value) { + uint64_t result = 0; + memcpy(&result, &value, sizeof(value)); + return result; } -static inline void alloc_float(double* &freg_args, - uint64_t* &stack_args, - double* end, - float data) +/* + * Allocation of function arguments to their appropriate place in registers + * if possible and then to the stack. Handling of 'that' argument which + * goes to register r0 is handled separately and does not belong here. + * + * Note that we are handling integer arguments and floating-point arguments + * identically, depending on which register area is passed to this function. + * + * 'reg_args' - pointer to the current position in the buffer, + * corresponding to the register arguments. + * 'reg_args_end' - pointer to the end of the registers argument + * buffer. + * 'stack_args' - pointer to the current position in the buffer, + * corresponding to the arguments on stack. + * 'data' - typed data to put on the stack. + */ +template<typename T> +static inline void alloc_arg(uint64_t* ®_args, + uint64_t* reg_args_end, + void* &stack_args, + T* data) { - if (freg_args < end) { - memcpy(freg_args, &data, sizeof(data)); - freg_args++; + if (reg_args < reg_args_end) { + *reg_args = normalize_arg(*data); + reg_args++; } else { - memcpy(stack_args, &data, sizeof(data)); - stack_args++; + // According to the ABI, types that are smaller than 8 bytes are + // passed in registers or 8-byte stack slots. This rule is only + // partially true on Apple platforms, where types smaller than 8 + // bytes occupy only the space they require on the stack and + // their stack slot must be properly aligned. +#ifdef __APPLE__ + const size_t aligned_size = sizeof(T); +#else + const size_t aligned_size = 8; +#endif + // Ensure the pointer is aligned for the type + uintptr_t addr = (reinterpret_cast<uintptr_t>(stack_args) + aligned_size - 1) & ~(aligned_size - 1); + memcpy(reinterpret_cast<void*>(addr), data, sizeof(T)); + // Point the stack to the next slot. + stack_args = reinterpret_cast<void*>(addr + aligned_size); } } - extern "C" void invoke_copy_to_stack(uint64_t* stk, uint64_t *end, uint32_t paramCount, nsXPTCVariant* s) { - uint64_t *ireg_args = stk; - uint64_t *ireg_end = ireg_args + 8; - double *freg_args = (double *)ireg_end; - double *freg_end = freg_args + 8; - uint64_t *stack_args = (uint64_t *)freg_end; + uint64_t* ireg_args = stk; + uint64_t* ireg_end = ireg_args + 8; + // Pun on integer and floating-point registers being the same size. + uint64_t* freg_args = ireg_end; + uint64_t* freg_end = freg_args + 8; + void* stack_args = freg_end; // leave room for 'that' argument in x0 ++ireg_args; for (uint32_t i = 0; i < paramCount; i++, s++) { - uint64_t word; - - if (s->IsPtrData()) { - word = (uint64_t)s->ptr; + if (s->IsIndirect()) { + void* ptr = &s->val; + alloc_arg(ireg_args, ireg_end, stack_args, &ptr); } else { - // According to the ABI, integral types that are smaller than 8 - // bytes are to be passed in 8-byte registers or 8-byte stack - // slots. switch (s->type) { case nsXPTType::T_FLOAT: - alloc_float(freg_args, stack_args, freg_end, s->val.f); - continue; + alloc_arg(freg_args, freg_end, stack_args, &s->val.f); + break; case nsXPTType::T_DOUBLE: - alloc_double(freg_args, stack_args, freg_end, s->val.d); - continue; - case nsXPTType::T_I8: word = s->val.i8; break; - case nsXPTType::T_I16: word = s->val.i16; break; - case nsXPTType::T_I32: word = s->val.i32; break; - case nsXPTType::T_I64: word = s->val.i64; break; - case nsXPTType::T_U8: word = s->val.u8; break; - case nsXPTType::T_U16: word = s->val.u16; break; - case nsXPTType::T_U32: word = s->val.u32; break; - case nsXPTType::T_U64: word = s->val.u64; break; - case nsXPTType::T_BOOL: word = s->val.b; break; - case nsXPTType::T_CHAR: word = s->val.c; break; - case nsXPTType::T_WCHAR: word = s->val.wc; break; + alloc_arg(freg_args, freg_end, stack_args, &s->val.d); + break; + case nsXPTType::T_I8: + alloc_arg(ireg_args, ireg_end, stack_args, &s->val.i8); + break; + case nsXPTType::T_I16: + alloc_arg(ireg_args, ireg_end, stack_args, &s->val.i16); + break; + case nsXPTType::T_I32: + alloc_arg(ireg_args, ireg_end, stack_args, &s->val.i32); + break; + case nsXPTType::T_I64: + alloc_arg(ireg_args, ireg_end, stack_args, &s->val.i64); + break; + case nsXPTType::T_U8: + alloc_arg(ireg_args, ireg_end, stack_args, &s->val.u8); + break; + case nsXPTType::T_U16: + alloc_arg(ireg_args, ireg_end, stack_args, &s->val.u16); + break; + case nsXPTType::T_U32: + alloc_arg(ireg_args, ireg_end, stack_args, &s->val.u32); + break; + case nsXPTType::T_U64: + alloc_arg(ireg_args, ireg_end, stack_args, &s->val.u64); + break; + case nsXPTType::T_BOOL: + alloc_arg(ireg_args, ireg_end, stack_args, &s->val.b); + break; + case nsXPTType::T_CHAR: + alloc_arg(ireg_args, ireg_end, stack_args, &s->val.c); + break; + case nsXPTType::T_WCHAR: + alloc_arg(ireg_args, ireg_end, stack_args, &s->val.wc); + break; default: // all the others are plain pointer types - word = reinterpret_cast<uint64_t>(s->val.p); + alloc_arg(ireg_args, ireg_end, stack_args, &s->val.p); break; } } - - alloc_word(ireg_args, stack_args, ireg_end, word); } } |