首页 > 解决方案 > 在使用 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

标签: c++iosxcode

解决方案


这很难追踪,但我想我找到了问题所在。首先,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都是

  1. 在类内部定义(因此是隐含的inline),或
  2. 在类外部定义但显式标记inline,或
  3. 未标记__attribute__((internal_linkage))

我相信这就是为什么这个问题只出现的原因shrink_to_fit()它是一个非内联函数,因为它是在其类定义之外定义的,尽管类是一个模板

具体来说:

  1. Chromium 应该考虑删除它们的显式实例化——这些很脆弱,正如这里所解释的那样。
  2. 我将修复 libc++ 的shrink_to_fit()问题inline。(编辑:这里
  3. 我将提交一个 Clang 错误来讨论当显式模板实例化声明出现在系统标头之外时发出该警告的可能性。(编辑:这里

感谢您报告这个棘手的问题。


推荐阅读