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