xref: /aosp_15_r20/external/cronet/base/native_library_mac.mm (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1// Copyright 2011 The Chromium Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/native_library.h"
6
7#include <dlfcn.h>
8#include <mach-o/getsect.h>
9
10#include "base/apple/scoped_cftyperef.h"
11#include "base/files/file_path.h"
12#include "base/files/file_util.h"
13#include "base/logging.h"
14#include "base/strings/strcat.h"
15#include "base/strings/string_piece.h"
16#include "base/strings/string_util.h"
17#include "base/strings/sys_string_conversions.h"
18#include "base/strings/utf_string_conversions.h"
19#include "base/threading/thread_restrictions.h"
20
21namespace base {
22
23static NativeLibraryObjCStatus GetObjCStatusForImage(
24    const void* function_pointer) {
25  Dl_info info;
26  if (!dladdr(function_pointer, &info))
27    return OBJC_UNKNOWN;
28
29  // See if the image contains an "ObjC image info" segment. This method
30  // of testing is used in _CFBundleGrokObjcImageInfoFromFile in
31  // CF-1153.18/CFBundle_Grok.c, around line 349.
32  //
33  // In 64-bit images, ObjC can be recognized in __DATA,__objc_imageinfo.
34  const auto* header =
35      reinterpret_cast<const struct mach_header_64*>(info.dli_fbase);
36  unsigned long size = 0;
37  getsectiondata(header, SEG_DATA, "__objc_imageinfo", &size);
38  if (size > 0) {
39    return OBJC_PRESENT;
40  }
41  // ....except when "SharedRegionEncodingV2" is on, it's in
42  // __DATA_CONST,__objc_image_info (see https://crbug.com/1220459#c16)
43  getsectiondata(header, "__DATA_CONST", "__objc_imageinfo", &size);
44  return size > 0 ? OBJC_PRESENT : OBJC_NOT_PRESENT;
45}
46
47std::string NativeLibraryLoadError::ToString() const {
48  return message;
49}
50
51NativeLibrary LoadNativeLibraryWithOptions(const FilePath& library_path,
52                                           const NativeLibraryOptions& options,
53                                           NativeLibraryLoadError* error) {
54  // dlopen() etc. open the file off disk.
55  if (library_path.Extension() == "dylib" || !DirectoryExists(library_path)) {
56    void* dylib = dlopen(library_path.value().c_str(), RTLD_LAZY);
57    if (!dylib) {
58      if (error)
59        error->message = dlerror();
60      return nullptr;
61    }
62    NativeLibrary native_lib = new NativeLibraryStruct();
63    native_lib->type = DYNAMIC_LIB;
64    native_lib->dylib = dylib;
65    native_lib->objc_status = OBJC_UNKNOWN;
66    return native_lib;
67  }
68  apple::ScopedCFTypeRef<CFURLRef> url(CFURLCreateFromFileSystemRepresentation(
69      kCFAllocatorDefault, (const UInt8*)library_path.value().c_str(),
70      checked_cast<CFIndex>(library_path.value().length()), true));
71  if (!url)
72    return nullptr;
73  CFBundleRef bundle = CFBundleCreate(kCFAllocatorDefault, url.get());
74  if (!bundle)
75    return nullptr;
76
77  NativeLibrary native_lib = new NativeLibraryStruct();
78  native_lib->type = BUNDLE;
79  native_lib->bundle = bundle;
80  native_lib->objc_status = OBJC_UNKNOWN;
81  return native_lib;
82}
83
84void UnloadNativeLibrary(NativeLibrary library) {
85  if (library->objc_status == OBJC_NOT_PRESENT) {
86    if (library->type == BUNDLE) {
87      CFRelease(library->bundle);
88    } else {
89      dlclose(library->dylib);
90    }
91  } else {
92    VLOG(2) << "Not unloading NativeLibrary because it may contain an ObjC "
93               "segment. library->objc_status = " << library->objc_status;
94    // Deliberately do not CFRelease the bundle or dlclose the dylib because
95    // doing so can corrupt the ObjC runtime method caches. See
96    // http://crbug.com/172319 for details.
97  }
98  delete library;
99}
100
101void* GetFunctionPointerFromNativeLibrary(NativeLibrary library,
102                                          const char* name) {
103  void* function_pointer = nullptr;
104
105  // Get the function pointer using the right API for the type.
106  if (library->type == BUNDLE) {
107    apple::ScopedCFTypeRef<CFStringRef> symbol_name =
108        SysUTF8ToCFStringRef(name);
109    function_pointer =
110        CFBundleGetFunctionPointerForName(library->bundle, symbol_name.get());
111  } else {
112    function_pointer = dlsym(library->dylib, name);
113  }
114
115  // If this library hasn't been tested for having ObjC, use the function
116  // pointer to look up the section information for the library.
117  if (function_pointer && library->objc_status == OBJC_UNKNOWN)
118    library->objc_status = GetObjCStatusForImage(function_pointer);
119
120  return function_pointer;
121}
122
123std::string GetNativeLibraryName(StringPiece name) {
124  DCHECK(IsStringASCII(name));
125  return StrCat({"lib", name, ".dylib"});
126}
127
128std::string GetLoadableModuleName(StringPiece name) {
129  DCHECK(IsStringASCII(name));
130  return StrCat({name, ".so"});
131}
132
133}  // namespace base
134