iOS std::string 因为内存分配器使用导致部分机型系统出现崩溃

不重复了

重现步骤
Exception Type: EXC_CRASH

Exception Codes: 0x0000000000000000, 0x0000000000000000

Exception signal: SIGABRT

triggered

thread queue 53771 com.apple.main-thread

0 libsystem_kernel.dylib 0x1e690c1dc 0x1e6901000+0xb1dc __pthread_kill+8

1 libsystem_pthread.dylib 0x21fc12c60 0x21fc0b000+0x7c60 pthread_kill+268

2 libsystem_c.dylib 0x19ccc92d0 0x19cc52000+0x772d0 abort+124

3 libsystem_malloc.dylib 0x1a560ad7c 0x1a55f4000+0x16d7c malloc_vreport+892

4 libsystem_malloc.dylib 0x1a560a9f4 0x1a55f4000+0x169f4 malloc_report+64

5 libsystem_malloc.dylib 0x1a56055a0 0x1a55f4000+0x115a0 ___BUG_IN_CLIENT_OF_LIBMALLOC_POINTER_BEING_FREED_WAS_NOT_ALLOCATED+32

6 libc++.1.dylib 0x1a58d4ac4 0x1a58bc000+0x18ac4 std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::~basic_string()+44

7 UAGame 0x105ef6d70 0x104ab4000+0x1442d70 G6ToolKit::google::protobuf::SimpleDescriptorDatabase::DescriptorIndex<std::__1::pair<void const*, int> >::AddFile(G6ToolKit::google::protobuf::FileDescriptorProto const&, std::__1::pair<void const*, int>) + 332+332

8 UAGame 0x105ef6b74 0x104ab4000+0x1442b74 G6ToolKit::google::protobuf::EncodedDescriptorDatabase::Add(void const*, int) + 108+108

9 UAGame 0x105ead898 0x104ab4000+0x13f9898 G6ToolKit::google::protobuf::DescriptorPool::InternalAddGeneratedFile(void const*, int) + 48+48

10 UAGame 0x105eddd6c 0x104ab4000+0x1429d6c G6ToolKit_protobuf_google_2fprotobuf_2fdescriptor_2eproto::AddDescriptorsImpl() + 28+28

11 UAGame 0x105f2c5dc 0x104ab4000+0x14785dc G6ToolKit::google::protobuf::internal::FunctionClosure0::Run() + 28+28

12 UAGame 0x105f2de04 0x104ab4000+0x1479e04 G6ToolKit::google::protobuf::GoogleOnceInitImpl(long*, G6ToolKit::google::protobuf::Closure*) + 112+112

13 UAGame 0x105fc7f80 0x104ab4000+0x1513f80 G6ToolKit::google::protobuf::GoogleOnceInit(long*, void (*)()) + 64+64

14 UAGame 0x105ef4d88 0x104ab4000+0x1440d88 G6ToolKit_protobuf_google_2fprotobuf_2fdescriptor_2eproto::StaticDescriptorInitializer::StaticDescriptorInitializer() + 20+20

15 dyld 0x1bc5ede58 0x1bc5b8000+0x35e58 invocation function for block in dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const+624

16 dyld 0x1bc62ac2c 0x1bc5b8000+0x72c2c invocation function for block in dyld3::MachOAnalyzer::forEachInitializer(Diagnostics&, dyld3::MachOAnalyzer::VMAddrConverter const&, void (unsigned int) block_pointer, void const*) const+172

​这是项目接入protobuf然后在iOS 18.5的开发版本出现的闪退,游戏启动不了,回想还有很多类似的a静态库,工作量不少,稳定性也没有保障

请问UE这边有提供比较权威的解决方案吗,华为的系统也有类似的问题,AI的建议是通过

// YourProject.Build.cs if (Target.Platform == UnrealTargetPlatform.IOS) { // 直接添加链接器参数 PublicAdditionalLibraries.Add("-Wl,-alias,_UE4Malloc,_malloc"); PublicAdditionalLibraries.Add("-Wl,-alias,_UE4Free,_free"); }// MyMemoryBridge.cpp

include “HAL/MemoryBase.h”

extern “C” {

void* UE4Malloc(size_t Size) {

return FMemory::Malloc(Size);

}

void UE4Free(void* Ptr) {

FMemory::Free(Ptr);

}

}

来替换a静态库的​连接解决问题,这个应该挺好的,不知道UE这边有提供这种方案吗

PublicAdditionalLibraries 这个我搜了下,好像没有相关API?

Hi,

你好,这是一个已知的问题,你可以先试试这个方案 https://github.com/EpicGames/UnrealEngine/commit/c82c70d4a5dd3f8c447fb3bf123d5ee447a1ea22 ,看看是否有效。

[Image Removed]按照上面的方案,我合入后出现这个报错,找不到Interpose的FrameWork,是还有哪些地方没有处理好吗?

另外这个的原理是啥?

按照方案是不是直接用这段代码放在任何地方就好了?

include <fcntl.h>

typedef int (*p_creat) (const char* path, mode_t mode);

static int replacement_creat(const char* path, mode_t mode)

{

return creat(path, mode);

}

__attribute__((used)) static struct{ p_creat replacement; p_creat replacee; } interpose_creat

__attribute__ ((section (“__DATA,__interpose”))) = { replacement_creat, creat };

不好意思,git的链接里没有包含二进制文件​,有些确实,请把下面的目录解压到\Engine\Source\ThirdParty\IOS里

我们还在尝试,不过有个问题

https://developer.apple.com/forums/thread/774805?answerId\=826017022\#826017022

这篇文章里面说

I’ve read the post: An Apple Library Primer, and found: “Dynamic linker interposing is not documented as API. While it’s a useful technique for developer tools, do not use it in products you ship to end users.”, so I guess this tricky way is still not recommended.

In the end, I think the solution is either to raise the deployment target to iOS 17, or totally remove the usage of the std stuff in libc++1.dylib.

说明这个方法是没有办法在发布版本使用的,请问UE这块有办法在发布版本用吗?

尝试了上面的方案,还是会闪退

:ok_hand: 目前我们找到了其他方法处理了,感谢支持

Hi,

这个workround我们的确没有在Fortnite里使用(可能也没有考虑到是否能在Shipping版使用的问题)。我后续会再跟相关同事确认一下。另外按理说MallocBinned里已经使用FPlatformMemory::PtrIsOSMalloc来判断内存是否直接是来自系统分配的,所以应该能处理这个情况。不过目前看起来没找到其他方案的话,这种设备上只能暂时用Ansi来做内存分配器了。

感谢反馈,不知道是否方便透露一下修复的方式或者思路?

1

mkdir temp_arm64 && cd temp_arm64

ar x ../libtoolkit

cd ..

2

cd temp_arm64

for obj_file in *.o; do

llvm\-objcopy \-\-redefine\-sym \_malloc\=\_UE4Malloc \\

             \-\-redefine\-sym \_free\=\_UE4Free \\

                             \-\-redefine\-sym \_\_Znwm\=\_UE4New \\

                             \-\-redefine\-sym \_\_ZdlPv\=\_UE4Delete \\

                             \-\-redefine\-sym \_\_Znam\=\_UE4NewArray \\

                             \-\-redefine\-sym \_\_ZdaPv\=\_UE4DeleteArray \\

                             \-\-redefine\-sym \_\_ZnwmRKSt9nothrow\_t\=\_UE4NewNoThrow \\

                             \-\-redefine\-sym \_\_ZnamRKSt9nothrow\_t\=\_UE4NewArrayNoThrow \\

                             \-\-redefine\-sym \_\_ZdlPvRKSt9nothrow\_t\=\_UE4DeleteNoThrow \\

                             \-\-redefine\-sym \_\_ZdaPvRKSt9nothrow\_t\=\_UE4DeleteArrayNoThrow \\

                             \-\-redefine\-sym \_\_ZdlPvm\=\_UE4DeleteSized \\

                             \-\-redefine\-sym \_\_ZdaPvm\=\_UE4DeleteArraySized \\

                             \-\-redefine\-sym \_\_ZdlPvmRKSt9nothrow\_t\=\_UE4DeleteSizedNoThrow \\

                             \-\-redefine\-sym \_\_ZdaPvmRKSt9nothrow\_t\=\_UE4DeleteArraySizedNoThrow \\

             $obj\_file

done

3

cd ..

ar rcs libtoolkit_new temp_arm64/*.o

4

查看结果

nm libtoolkit_new | grep -E “UE4NewArray|UE4DeleteArray”

或者

cd

cd temp_arm64

nm *.o | grep “_UE4Malloc\|_UE4Free”

替换静态库的

OPERATOR_NEW_MSVC_PRAGMA void* operator new ( size_t Size ) OPERATOR_NEW_THROW_SPEC { return FMemory::Malloc( Size ); } \

OPERATOR_NEW_MSVC_PRAGMA void* operator new( size_t Size ) OPERATOR_NEW_THROW_SPEC { return FMemory::Malloc( Size ); } \

OPERATOR_NEW_MSVC_PRAGMA void* operator new ( size_t Size, const std::nothrow_t& ) OPERATOR_NEW_NOTHROW_SPEC { return FMemory::Malloc( Size ); } \

OPERATOR_NEW_MSVC_PRAGMA void* operator new( size_t Size, const std::nothrow_t& ) OPERATOR_NEW_NOTHROW_SPEC { return FMemory::Malloc( Size ); } \

void operator delete ( void* Ptr ) OPERATOR_DELETE_THROW_SPEC { FMemory::Free( Ptr ); } \

void operator delete( void* Ptr ) OPERATOR_DELETE_THROW_SPEC { FMemory::Free( Ptr ); } \

void operator delete ( void* Ptr, const std::nothrow_t& ) OPERATOR_DELETE_NOTHROW_SPEC { FMemory::Free( Ptr ); } \

void operator delete( void* Ptr, const std::nothrow_t& ) OPERATOR_DELETE_NOTHROW_SPEC { FMemory::Free( Ptr ); } \

void operator delete ( void* Ptr, size_t Size ) OPERATOR_DELETE_THROW_SPEC { FMemory::Free( Ptr ); } \

void operator delete( void* Ptr, size_t Size ) OPERATOR_DELETE_THROW_SPEC { FMemory::Free( Ptr ); } \

void operator delete ( void* Ptr, size_t Size, const std::nothrow_t& ) OPERATOR_DELETE_NOTHROW_SPEC { FMemory::Free( Ptr ); } \

void operator delete( void* Ptr, size_t Size, const std::nothrow_t& ) OPERATOR_DELETE_NOTHROW_SPEC { FMemory::Free( Ptr ); }

自己写桥接函数,然后用malloc\free替代

非常感谢。