c++ - 在使用 Xcode 13 在 iOS 15 上编译 Cronet 时,shrink_to_fit() 的“未定义符号”
问题描述
错误信息:
Undefined symbols for architecture arm64:
"std::__1::basic_string<unsigned short, base::string16_internals::string16_char_traits, std::__1::allocator<unsigned short> >::shrink_to_fit()", referenced from:
base::UTF8ToUTF16(char const*, unsigned long, std::__1::basic_string<unsigned short, base::string16_internals::string16_char_traits, std::__1::allocator<unsigned short> >*) in libbase.a(utf_string_conversions.o)
base::WideToUTF16(wchar_t const*, unsigned long, std::__1::basic_string<unsigned short, base::string16_internals::string16_char_traits, std::__1::allocator<unsigned short> >*) in libbase.a(utf_string_conversions.o)
ld: symbol(s) not found for architecture arm64
subprocess.CalledProcessError: Command '['clang++', '-B', '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/', '-shared', '-Xlinker', '-install_name', '-Xlinker', '@rpath/Cronet.framework/Cronet', '-Xlinker', '-objc_abi_version', '-Xlinker', '2', '-arch', 'arm64', '-Werror', '-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.0.sdk', '-stdlib=libc++', '-miphoneos-version-min=8.0', '-fembed-bitcode', '-Wl,-ObjC', '-o', 'obj/components/cronet/ios/arm64/Cronet', '-Wl,-filelist,obj/components/cronet/ios/arm64/Cronet.rsp', '-framework', 'UIKit', '-framework', 'CoreFoundation', '-framework', 'CoreGraphics', '-framework', 'CoreText', '-framework', 'Foundation', '-framework', 'JavaScriptCore', '-framework', 'CFNetwork', '-framework', 'MobileCoreServices', '-framework', 'Security', '-framework', 'SystemConfiguration', '-lresolv']' returned non-zero exit status 1
解决方案
这很难追踪,但我想我找到了问题所在。首先,string16.ii
可以简化为:
template <class T>
struct basic_string {
__attribute__((internal_linkage))
void shrink_to_fit();
};
template <class T>
void basic_string<T>::shrink_to_fit() { }
template class basic_string<char>;
utf_string_conversions.ii
可以简化为:
template <class T>
struct basic_string {
__attribute__((internal_linkage))
void shrink_to_fit();
};
template <class T>
void basic_string<T>::shrink_to_fit() { }
extern template class basic_string<char>;
int main() {
basic_string<char> s;
s.shrink_to_fit();
}
由于shrink_to_fit
具有内部链接,编译器甚至不会费心在内部发出它string16.o
,因为它没有在那个 TU 中使用。如果它确实在 中发出它string16.o
,那将是死代码,因为无论如何其他 TU 都无法引用它。
然后,从utf_string_conversions.o
中,我们看到了一个 extern 模板实例化声明,它基本上保证了我们将能够shrink_to_fit()
在其他一些 TU 中找到(大概是string16.o
)。当然,情况并非如此,因为另一个 TU 不能“导出” shrink_to_fit
,它具有如上所述的内部链接。
结果,我们最终处于utf_string_conversions.o
期望shrink_to_fit()
在其他一些 TU 中看到的情况,但应该提供它的其他 TU 没有。此外,如果我们编译上面的代码,我们实际上可以看到编译器正在警告我们:
<stdin>:4:10: warning: function 'basic_string<char>::shrink_to_fit' has internal linkage but is not defined [-Wundefined-internal]
void shrink_to_fit();
^
<stdin>:14:7: note: used here
s.shrink_to_fit();
^
1 warning generated.
Undefined symbols for architecture x86_64:
"basic_string<char>::shrink_to_fit()", referenced from:
_main in main.o
ld: symbol(s) not found for architecture x86_64
此警告未显示在原始代码中,因为系统标头内的警告被禁止。最初这确实让我感到困惑,我认为编译器警告显式模板实例化声明是否出现在系统头文件之外是有意义的,无论类本身是否在系统头文件中声明。我实际上从原始复制器中删除了系统标头指令,并且我能够得到相同的警告,请参阅this。
现在,您可能想知道为什么其他方法不会发生这种情况basic_string
?好吧,显然,大多数(如果不是所有)其他方法basic_string
都是
- 在类内部定义(因此是隐含的
inline
),或 - 在类外部定义但显式标记
inline
,或 - 未标记
__attribute__((internal_linkage))
我相信这就是为什么这个问题只出现的原因shrink_to_fit()
:它是一个非内联函数,因为它是在其类定义之外定义的,尽管类是一个模板。
具体来说:
- Chromium 应该考虑删除它们的显式实例化——这些很脆弱,正如这里所解释的那样。
- 我将修复 libc++ 的
shrink_to_fit()
问题inline
。(编辑:这里) - 我将提交一个 Clang 错误来讨论当显式模板实例化声明出现在系统标头之外时发出该警告的可能性。(编辑:这里)
感谢您报告这个棘手的问题。
推荐阅读
- google-cloud-dataflow - Google Cloud Dataflow 因启用 compute.requireShieldedVm 而失败
- c# - 在c#中,当我们在方法逻辑中使用Task.Run()在后台运行线程时,是否必须使用async关键字来调用方法?
- c - 在由参数传递的指针上的函数内使用 malloc
- wordpress - wordpress 插件 gogole 地图出现错误:ApiNotActivatedMapError
- c# - Open XML:我们发现“myfile.xlsx”中的某些内容存在问题
- python - 如何从 ManyToOne 表中只获取一个对象,而不是查询集?
- image - 如何将图像保存到 playerprefs 并将它们显示到另一个场景?
- oledb - CLR 存储过程,对列名长度施加的限制
- java - 使用嵌套的 for 循环从用户输入中打印给定的数字模式
- asp.net-core - Masstransit RabbitMQ 发布者未能发布“正确”合约