-
Notifications
You must be signed in to change notification settings - Fork 4.7k
/
nativelibrary.cpp
909 lines (746 loc) · 32.2 KB
/
nativelibrary.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#include "common.h"
#include "nativelibrary.h"
#include "clr/fs/path.h"
using namespace clr::fs;
// Specifies whether hostpolicy is embedded in executable or standalone
extern bool g_hostpolicy_embedded;
// remove when we get an updated SDK
#define LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR 0x00000100
#ifdef TARGET_UNIX
#define PLATFORM_SHARED_LIB_SUFFIX_W PAL_SHLIB_SUFFIX_W
#define PLATFORM_SHARED_LIB_PREFIX_W PAL_SHLIB_PREFIX_W
#else // !TARGET_UNIX
#define PLATFORM_SHARED_LIB_SUFFIX_W W(".dll")
#define PLATFORM_SHARED_LIB_PREFIX_W W("")
#endif // !TARGET_UNIX
// The Bit 0x2 has different semantics in DllImportSearchPath and LoadLibraryExA flags.
// In DllImportSearchPath enum, bit 0x2 represents SearchAssemblyDirectory -- which is performed by CLR.
// Unlike other bits in this enum, this bit shouldn't be directly passed on to LoadLibrary()
#define DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY 0x2
namespace
{
// Preserving good error info from DllImport-driven LoadLibrary is tricky because we keep loading from different places
// if earlier loads fail and those later loads obliterate error codes.
//
// This tracker object will keep track of the error code in accordance to priority:
//
// low-priority: unknown error code (should never happen)
// medium-priority: dll not found
// high-priority: dll found but error during loading
//
// We will overwrite the previous load's error code only if the new error code is higher priority.
//
class LoadLibErrorTracker
{
private:
static const DWORD const_priorityNotFound = 10;
static const DWORD const_priorityAccessDenied = 20;
static const DWORD const_priorityCouldNotLoad = 99999;
public:
LoadLibErrorTracker()
{
LIMITED_METHOD_CONTRACT;
m_hr = E_FAIL;
m_priorityOfLastError = 0;
}
VOID TrackErrorCode()
{
LIMITED_METHOD_CONTRACT;
DWORD priority;
#ifdef TARGET_UNIX
SetMessage(PAL_GetLoadLibraryError());
#else
DWORD dwLastError = GetLastError();
switch (dwLastError)
{
case ERROR_FILE_NOT_FOUND:
case ERROR_PATH_NOT_FOUND:
case ERROR_MOD_NOT_FOUND:
case ERROR_DLL_NOT_FOUND:
priority = const_priorityNotFound;
break;
// If we can't access a location, we can't know if the dll's there or if it's good.
// Still, this is probably more unusual (and thus of more interest) than a dll-not-found
// so give it an intermediate priority.
case ERROR_ACCESS_DENIED:
priority = const_priorityAccessDenied;
// Assume all others are "dll found but couldn't load."
default:
priority = const_priorityCouldNotLoad;
break;
}
UpdateHR(priority, HRESULT_FROM_WIN32(dwLastError));
#endif
}
HRESULT GetHR()
{
return m_hr;
}
SString& GetMessage()
{
return m_message;
}
void DECLSPEC_NORETURN Throw(SString &libraryNameOrPath)
{
STANDARD_VM_CONTRACT;
#if defined(__APPLE__)
COMPlusThrow(kDllNotFoundException, IDS_EE_NDIRECT_LOADLIB_MAC, libraryNameOrPath.GetUnicode(), GetMessage());
#elif defined(TARGET_UNIX)
COMPlusThrow(kDllNotFoundException, IDS_EE_NDIRECT_LOADLIB_LINUX, libraryNameOrPath.GetUnicode(), GetMessage());
#else // __APPLE__
HRESULT theHRESULT = GetHR();
if (theHRESULT == HRESULT_FROM_WIN32(ERROR_BAD_EXE_FORMAT))
{
COMPlusThrow(kBadImageFormatException);
}
else
{
SString hrString;
GetHRMsg(theHRESULT, hrString);
COMPlusThrow(kDllNotFoundException, IDS_EE_NDIRECT_LOADLIB_WIN, libraryNameOrPath.GetUnicode(), hrString);
}
#endif // TARGET_UNIX
__UNREACHABLE();
}
private:
void UpdateHR(DWORD priority, HRESULT hr)
{
if (priority > m_priorityOfLastError)
{
m_hr = hr;
m_priorityOfLastError = priority;
}
}
void SetMessage(LPCSTR message)
{
m_message = SString(SString::Utf8, message);
}
HRESULT m_hr;
DWORD m_priorityOfLastError;
SString m_message;
}; // class LoadLibErrorTracker
// Load the library directly and return the raw system handle
NATIVE_LIBRARY_HANDLE LocalLoadLibraryHelper( LPCWSTR name, DWORD flags, LoadLibErrorTracker *pErrorTracker )
{
STANDARD_VM_CONTRACT;
NATIVE_LIBRARY_HANDLE hmod = NULL;
#ifndef TARGET_UNIX
if ((flags & 0xFFFFFF00) != 0)
{
hmod = CLRLoadLibraryEx(name, NULL, flags & 0xFFFFFF00);
if (hmod != NULL)
{
return hmod;
}
DWORD dwLastError = GetLastError();
if (dwLastError != ERROR_INVALID_PARAMETER)
{
pErrorTracker->TrackErrorCode();
return hmod;
}
}
hmod = CLRLoadLibraryEx(name, NULL, flags & 0xFF);
#else // !TARGET_UNIX
hmod = PAL_LoadLibraryDirect(name);
#endif // !TARGET_UNIX
if (hmod == NULL)
{
pErrorTracker->TrackErrorCode();
}
return hmod;
}
// DllImportSearchPathFlags is a special enumeration, whose values are tied closely with LoadLibrary flags.
// There is no "default" value DllImportSearchPathFlags. In the absence of DllImportSearchPath attribute,
// CoreCLR's LoadLibrary implementation uses the following defaults.
// Other implementations of LoadLibrary callbacks/events are free to use other default conventions.
void GetDefaultDllImportSearchPathFlags(DWORD *dllImportSearchPathFlags, BOOL *searchAssemblyDirectory)
{
STANDARD_VM_CONTRACT;
*searchAssemblyDirectory = TRUE;
*dllImportSearchPathFlags = 0;
}
// If a module has the DefaultDllImportSearchPathsAttribute, get DllImportSearchPathFlags from it, and return true.
// Otherwise, get CoreCLR's default value for DllImportSearchPathFlags, and return false.
BOOL GetDllImportSearchPathFlags(Module *pModule, DWORD *dllImportSearchPathFlags, BOOL *searchAssemblyDirectory)
{
STANDARD_VM_CONTRACT;
if (pModule->HasDefaultDllImportSearchPathsAttribute())
{
*dllImportSearchPathFlags = pModule->DefaultDllImportSearchPathsAttributeCachedValue();
*searchAssemblyDirectory = pModule->DllImportSearchAssemblyDirectory();
return TRUE;
}
GetDefaultDllImportSearchPathFlags(dllImportSearchPathFlags, searchAssemblyDirectory);
return FALSE;
}
// If a pInvoke has the DefaultDllImportSearchPathsAttribute, get DllImportSearchPathFlags from it, and returns true.
// Otherwise, if the containing assembly has the DefaultDllImportSearchPathsAttribute, get DllImportSearchPathFlags from it, and returns true.
// Otherwise, get CoreCLR's default value for DllImportSearchPathFlags, and return false.
BOOL GetDllImportSearchPathFlags(NDirectMethodDesc * pMD, DWORD *dllImportSearchPathFlags, BOOL *searchAssemblyDirectory)
{
STANDARD_VM_CONTRACT;
if (pMD->HasDefaultDllImportSearchPathsAttribute())
{
*dllImportSearchPathFlags = pMD->DefaultDllImportSearchPathsAttributeCachedValue();
*searchAssemblyDirectory = pMD->DllImportSearchAssemblyDirectory();
return TRUE;
}
return GetDllImportSearchPathFlags(pMD->GetModule(), dllImportSearchPathFlags, searchAssemblyDirectory);
}
}
// static
NATIVE_LIBRARY_HANDLE NativeLibrary::LoadLibraryFromPath(LPCWSTR libraryPath, BOOL throwOnError)
{
CONTRACTL
{
STANDARD_VM_CHECK;
PRECONDITION(CheckPointer(libraryPath));
}
CONTRACTL_END;
LoadLibErrorTracker errorTracker;
const NATIVE_LIBRARY_HANDLE hmod =
LocalLoadLibraryHelper(libraryPath, GetLoadWithAlteredSearchPathFlag(), &errorTracker);
if (throwOnError && (hmod == nullptr))
{
SString libraryPathSString(libraryPath);
errorTracker.Throw(libraryPathSString);
}
return hmod;
}
// static
void NativeLibrary::FreeNativeLibrary(NATIVE_LIBRARY_HANDLE handle)
{
STANDARD_VM_CONTRACT;
_ASSERTE(handle != NULL);
#ifndef TARGET_UNIX
BOOL retVal = FreeLibrary(handle);
#else // !TARGET_UNIX
BOOL retVal = PAL_FreeLibraryDirect(handle);
#endif // !TARGET_UNIX
if (retVal == 0)
COMPlusThrow(kInvalidOperationException, W("Arg_InvalidOperationException"));
}
//static
INT_PTR NativeLibrary::GetNativeLibraryExport(NATIVE_LIBRARY_HANDLE handle, LPCWSTR symbolName, BOOL throwOnError)
{
CONTRACTL
{
STANDARD_VM_CHECK;
PRECONDITION(CheckPointer(handle));
PRECONDITION(CheckPointer(symbolName));
}
CONTRACTL_END;
MAKE_UTF8PTR_FROMWIDE(lpstr, symbolName);
#ifndef TARGET_UNIX
INT_PTR address = reinterpret_cast<INT_PTR>(GetProcAddress((HMODULE)handle, lpstr));
if ((address == NULL) && throwOnError)
COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDR_WIN_DLL, symbolName);
#else // !TARGET_UNIX
INT_PTR address = reinterpret_cast<INT_PTR>(PAL_GetProcAddressDirect(handle, lpstr));
if ((address == NULL) && throwOnError)
COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDR_UNIX_SO, symbolName);
#endif // !TARGET_UNIX
return address;
}
namespace
{
#ifndef TARGET_UNIX
BOOL IsWindowsAPISet(PCWSTR wszLibName)
{
STANDARD_VM_CONTRACT;
// This is replicating quick check from the OS implementation of api sets.
return SString::_wcsnicmp(wszLibName, W("api-"), 4) == 0 ||
SString::_wcsnicmp(wszLibName, W("ext-"), 4) == 0;
}
#endif // !TARGET_UNIX
NATIVE_LIBRARY_HANDLE LoadNativeLibraryViaAssemblyLoadContext(Assembly * pAssembly, PCWSTR wszLibName)
{
STANDARD_VM_CONTRACT;
#ifndef TARGET_UNIX
if (IsWindowsAPISet(wszLibName))
{
// Prevent Overriding of Windows API sets.
return NULL;
}
#endif // !TARGET_UNIX
NATIVE_LIBRARY_HANDLE hmod = NULL;
AppDomain* pDomain = GetAppDomain();
CLRPrivBinderCoreCLR *pTPABinder = pDomain->GetTPABinderContext();
PEFile *pManifestFile = pAssembly->GetManifestFile();
PTR_ICLRPrivBinder pBindingContext = pManifestFile->GetBindingContext();
//Step 0: Check if the assembly was bound using TPA.
// The Binding Context can be null or an overridden TPA context
if (pBindingContext == NULL)
{
// If we do not have any binder associated, then return to the default resolution mechanism.
return NULL;
}
UINT_PTR assemblyBinderID = 0;
IfFailThrow(pBindingContext->GetBinderID(&assemblyBinderID));
ICLRPrivBinder *pCurrentBinder = reinterpret_cast<ICLRPrivBinder *>(assemblyBinderID);
// For assemblies bound via TPA binder, we should use the standard mechanism to make the pinvoke call.
if (AreSameBinderInstance(pCurrentBinder, pTPABinder))
{
return NULL;
}
//Step 1: If the assembly was not bound using TPA,
// Call System.Runtime.Loader.AssemblyLoadContext.ResolveUnmanagedDll to give
// The custom assembly context a chance to load the unmanaged dll.
GCX_COOP();
STRINGREF pUnmanagedDllName;
pUnmanagedDllName = StringObject::NewString(wszLibName);
GCPROTECT_BEGIN(pUnmanagedDllName);
// Get the pointer to the managed assembly load context
INT_PTR ptrManagedAssemblyLoadContext = ((CLRPrivBinderAssemblyLoadContext *)pCurrentBinder)->GetManagedAssemblyLoadContext();
// Prepare to invoke System.Runtime.Loader.AssemblyLoadContext.ResolveUnmanagedDll method.
PREPARE_NONVIRTUAL_CALLSITE(METHOD__ASSEMBLYLOADCONTEXT__RESOLVEUNMANAGEDDLL);
DECLARE_ARGHOLDER_ARRAY(args, 2);
args[ARGNUM_0] = STRINGREF_TO_ARGHOLDER(pUnmanagedDllName);
args[ARGNUM_1] = PTR_TO_ARGHOLDER(ptrManagedAssemblyLoadContext);
// Make the call
CALL_MANAGED_METHOD(hmod, NATIVE_LIBRARY_HANDLE, args);
GCPROTECT_END();
return hmod;
}
// Return the AssemblyLoadContext for an assembly
INT_PTR GetManagedAssemblyLoadContext(Assembly* pAssembly)
{
STANDARD_VM_CONTRACT;
PTR_ICLRPrivBinder pBindingContext = pAssembly->GetManifestFile()->GetBindingContext();
if (pBindingContext == NULL)
{
// GetBindingContext() returns NULL for System.Private.CoreLib
return NULL;
}
UINT_PTR assemblyBinderID = 0;
IfFailThrow(pBindingContext->GetBinderID(&assemblyBinderID));
AppDomain *pDomain = GetAppDomain();
ICLRPrivBinder *pCurrentBinder = reinterpret_cast<ICLRPrivBinder *>(assemblyBinderID);
// The code here deals with two implementations of ICLRPrivBinder interface:
// - CLRPrivBinderCoreCLR for the TPA binder in the default ALC, and
// - CLRPrivBinderAssemblyLoadContext for custom ALCs.
// in order obtain the associated ALC handle.
INT_PTR ptrManagedAssemblyLoadContext = AreSameBinderInstance(pCurrentBinder, pDomain->GetTPABinderContext())
? ((CLRPrivBinderCoreCLR *)pCurrentBinder)->GetManagedAssemblyLoadContext()
: ((CLRPrivBinderAssemblyLoadContext *)pCurrentBinder)->GetManagedAssemblyLoadContext();
return ptrManagedAssemblyLoadContext;
}
NATIVE_LIBRARY_HANDLE LoadNativeLibraryViaAssemblyLoadContextEvent(Assembly * pAssembly, PCWSTR wszLibName)
{
STANDARD_VM_CONTRACT;
INT_PTR ptrManagedAssemblyLoadContext = GetManagedAssemblyLoadContext(pAssembly);
if (ptrManagedAssemblyLoadContext == NULL)
{
return NULL;
}
NATIVE_LIBRARY_HANDLE hmod = NULL;
GCX_COOP();
struct {
STRINGREF DllName;
OBJECTREF AssemblyRef;
} gc = { NULL, NULL };
GCPROTECT_BEGIN(gc);
gc.DllName = StringObject::NewString(wszLibName);
gc.AssemblyRef = pAssembly->GetExposedObject();
// Prepare to invoke System.Runtime.Loader.AssemblyLoadContext.ResolveUnmanagedDllUsingEvent method
// While ResolveUnmanagedDllUsingEvent() could compute the AssemblyLoadContext using the AssemblyRef
// argument, it will involve another pInvoke to the runtime. So AssemblyLoadContext is passed in
// as an additional argument.
PREPARE_NONVIRTUAL_CALLSITE(METHOD__ASSEMBLYLOADCONTEXT__RESOLVEUNMANAGEDDLLUSINGEVENT);
DECLARE_ARGHOLDER_ARRAY(args, 3);
args[ARGNUM_0] = STRINGREF_TO_ARGHOLDER(gc.DllName);
args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(gc.AssemblyRef);
args[ARGNUM_2] = PTR_TO_ARGHOLDER(ptrManagedAssemblyLoadContext);
// Make the call
CALL_MANAGED_METHOD(hmod, NATIVE_LIBRARY_HANDLE, args);
GCPROTECT_END();
return hmod;
}
NATIVE_LIBRARY_HANDLE LoadNativeLibraryViaDllImportResolver(NDirectMethodDesc * pMD, LPCWSTR wszLibName)
{
STANDARD_VM_CONTRACT;
if (pMD->GetModule()->IsSystem())
{
// Don't attempt to callback on Corelib itself.
// The LoadLibrary callback stub is managed code that requires CoreLib
return NULL;
}
DWORD dllImportSearchPathFlags;
BOOL searchAssemblyDirectory;
BOOL hasDllImportSearchPathFlags = GetDllImportSearchPathFlags(pMD, &dllImportSearchPathFlags, &searchAssemblyDirectory);
dllImportSearchPathFlags |= searchAssemblyDirectory ? DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY : 0;
Assembly* pAssembly = pMD->GetMethodTable()->GetAssembly();
NATIVE_LIBRARY_HANDLE handle = NULL;
GCX_COOP();
struct {
STRINGREF libNameRef;
OBJECTREF assemblyRef;
} gc = { NULL, NULL };
GCPROTECT_BEGIN(gc);
gc.libNameRef = StringObject::NewString(wszLibName);
gc.assemblyRef = pAssembly->GetExposedObject();
PREPARE_NONVIRTUAL_CALLSITE(METHOD__NATIVELIBRARY__LOADLIBRARYCALLBACKSTUB);
DECLARE_ARGHOLDER_ARRAY(args, 4);
args[ARGNUM_0] = STRINGREF_TO_ARGHOLDER(gc.libNameRef);
args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(gc.assemblyRef);
args[ARGNUM_2] = BOOL_TO_ARGHOLDER(hasDllImportSearchPathFlags);
args[ARGNUM_3] = DWORD_TO_ARGHOLDER(dllImportSearchPathFlags);
// Make the call
CALL_MANAGED_METHOD(handle, NATIVE_LIBRARY_HANDLE, args);
GCPROTECT_END();
return handle;
}
// Try to load the module alongside the assembly where the PInvoke was declared.
NATIVE_LIBRARY_HANDLE LoadFromPInvokeAssemblyDirectory(Assembly *pAssembly, LPCWSTR libName, DWORD flags, LoadLibErrorTracker *pErrorTracker)
{
STANDARD_VM_CONTRACT;
NATIVE_LIBRARY_HANDLE hmod = NULL;
SString path = pAssembly->GetManifestFile()->GetPath();
SString::Iterator lastPathSeparatorIter = path.End();
if (PEAssembly::FindLastPathSeparator(path, lastPathSeparatorIter))
{
lastPathSeparatorIter++;
path.Truncate(lastPathSeparatorIter);
path.Append(libName);
hmod = LocalLoadLibraryHelper(path, flags, pErrorTracker);
}
return hmod;
}
// Try to load the module from the native DLL search directories
NATIVE_LIBRARY_HANDLE LoadFromNativeDllSearchDirectories(LPCWSTR libName, DWORD flags, LoadLibErrorTracker *pErrorTracker)
{
STANDARD_VM_CONTRACT;
NATIVE_LIBRARY_HANDLE hmod = NULL;
AppDomain* pDomain = GetAppDomain();
if (pDomain->HasNativeDllSearchDirectories())
{
AppDomain::PathIterator pathIter = pDomain->IterateNativeDllSearchDirectories();
while (hmod == NULL && pathIter.Next())
{
SString qualifiedPath(*(pathIter.GetPath()));
qualifiedPath.Append(libName);
if (!Path::IsRelative(qualifiedPath))
{
hmod = LocalLoadLibraryHelper(qualifiedPath, flags, pErrorTracker);
}
}
}
return hmod;
}
#ifdef TARGET_UNIX
const int MaxVariationCount = 4;
void DetermineLibNameVariations(const WCHAR** libNameVariations, int* numberOfVariations, const SString& libName, bool libNameIsRelativePath)
{
// Supported lib name variations
static auto NameFmt = W("%.0s%s%.0s");
static auto PrefixNameFmt = W("%s%s%.0s");
static auto NameSuffixFmt = W("%.0s%s%s");
static auto PrefixNameSuffixFmt = W("%s%s%s");
_ASSERTE(*numberOfVariations >= MaxVariationCount);
int varCount = 0;
if (!libNameIsRelativePath)
{
libNameVariations[varCount++] = NameFmt;
}
else
{
// We check if the suffix is contained in the name, because on Linux it is common to append
// a version number to the library name (e.g. 'libicuuc.so.57').
bool containsSuffix = false;
SString::CIterator it = libName.Begin();
if (libName.Find(it, PLATFORM_SHARED_LIB_SUFFIX_W))
{
it += COUNTOF(PLATFORM_SHARED_LIB_SUFFIX_W);
containsSuffix = it == libName.End() || *it == (WCHAR)'.';
}
// If the path contains a path delimiter, we don't add a prefix
it = libName.Begin();
bool containsDelim = libName.Find(it, DIRECTORY_SEPARATOR_STR_W);
if (containsSuffix)
{
libNameVariations[varCount++] = NameFmt;
if (!containsDelim)
libNameVariations[varCount++] = PrefixNameFmt;
libNameVariations[varCount++] = NameSuffixFmt;
if (!containsDelim)
libNameVariations[varCount++] = PrefixNameSuffixFmt;
}
else
{
libNameVariations[varCount++] = NameSuffixFmt;
if (!containsDelim)
libNameVariations[varCount++] = PrefixNameSuffixFmt;
libNameVariations[varCount++] = NameFmt;
if (!containsDelim)
libNameVariations[varCount++] = PrefixNameFmt;
}
}
*numberOfVariations = varCount;
}
#else // TARGET_UNIX
const int MaxVariationCount = 2;
void DetermineLibNameVariations(const WCHAR** libNameVariations, int* numberOfVariations, const SString& libName, bool libNameIsRelativePath)
{
// Supported lib name variations
static auto NameFmt = W("%.0s%s%.0s");
static auto NameSuffixFmt = W("%.0s%s%s");
_ASSERTE(*numberOfVariations >= MaxVariationCount);
int varCount = 0;
// The purpose of following code is to workaround LoadLibrary limitation:
// LoadLibrary won't append extension if filename itself contains '.'. Thus it will break the following scenario:
// [DllImport("A.B")] // The full name for file is "A.B.dll". This is common code pattern for cross-platform PInvoke
// The workaround for above scenario is to call LoadLibrary with "A.B" first, if it fails, then call LoadLibrary with "A.B.dll"
auto it = libName.Begin();
if (!libNameIsRelativePath ||
!libName.Find(it, W('.')) ||
libName.EndsWith(W(".")) ||
libName.EndsWithCaseInsensitive(W(".dll")) ||
libName.EndsWithCaseInsensitive(W(".exe")))
{
// Follow LoadLibrary rules in MSDN doc: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684175(v=vs.85).aspx
// If the string specifies a full path, the function searches only that path for the module.
// If the string specifies a module name without a path and the file name extension is omitted, the function appends the default library extension .dll to the module name.
// To prevent the function from appending .dll to the module name, include a trailing point character (.) in the module name string.
libNameVariations[varCount++] = NameFmt;
}
else
{
libNameVariations[varCount++] = NameFmt;
libNameVariations[varCount++] = NameSuffixFmt;
}
*numberOfVariations = varCount;
}
#endif // TARGET_UNIX
// Search for the library and variants of its name in probing directories.
NATIVE_LIBRARY_HANDLE LoadNativeLibraryBySearch(Assembly *callingAssembly,
BOOL searchAssemblyDirectory, DWORD dllImportSearchPathFlags,
LoadLibErrorTracker * pErrorTracker, LPCWSTR wszLibName)
{
STANDARD_VM_CONTRACT;
NATIVE_LIBRARY_HANDLE hmod = NULL;
#if defined(FEATURE_CORESYSTEM) && !defined(TARGET_UNIX)
// Try to go straight to System32 for Windows API sets. This is replicating quick check from
// the OS implementation of api sets.
if (IsWindowsAPISet(wszLibName))
{
hmod = LocalLoadLibraryHelper(wszLibName, LOAD_LIBRARY_SEARCH_SYSTEM32, pErrorTracker);
if (hmod != NULL)
{
return hmod;
}
}
#endif // FEATURE_CORESYSTEM && !TARGET_UNIX
if (g_hostpolicy_embedded)
{
#ifdef TARGET_WINDOWS
if (wcscmp(wszLibName, W("hostpolicy.dll")) == 0)
{
return WszGetModuleHandle(NULL);
}
#else
if (wcscmp(wszLibName, W("libhostpolicy")) == 0)
{
return PAL_LoadLibraryDirect(NULL);
}
#endif
}
AppDomain* pDomain = GetAppDomain();
DWORD loadWithAlteredPathFlags = GetLoadWithAlteredSearchPathFlag();
bool libNameIsRelativePath = Path::IsRelative(wszLibName);
// P/Invokes are often declared with variations on the actual library name.
// For example, it's common to leave off the extension/suffix of the library
// even if it has one, or to leave off a prefix like "lib" even if it has one
// (both of these are typically done to smooth over cross-platform differences).
// We try to dlopen with such variations on the original.
const WCHAR* prefixSuffixCombinations[MaxVariationCount] = {};
int numberOfVariations = COUNTOF(prefixSuffixCombinations);
DetermineLibNameVariations(prefixSuffixCombinations, &numberOfVariations, wszLibName, libNameIsRelativePath);
for (int i = 0; i < numberOfVariations; i++)
{
SString currLibNameVariation;
currLibNameVariation.Printf(prefixSuffixCombinations[i], PLATFORM_SHARED_LIB_PREFIX_W, wszLibName, PLATFORM_SHARED_LIB_SUFFIX_W);
// NATIVE_DLL_SEARCH_DIRECTORIES set by host is considered well known path
hmod = LoadFromNativeDllSearchDirectories(currLibNameVariation, loadWithAlteredPathFlags, pErrorTracker);
if (hmod != NULL)
{
return hmod;
}
if (!libNameIsRelativePath)
{
DWORD flags = loadWithAlteredPathFlags;
if ((dllImportSearchPathFlags & LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR) != 0)
{
// LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR is the only flag affecting absolute path. Don't OR the flags
// unconditionally as all absolute path P/Invokes could then lose LOAD_WITH_ALTERED_SEARCH_PATH.
flags |= dllImportSearchPathFlags;
}
hmod = LocalLoadLibraryHelper(currLibNameVariation, flags, pErrorTracker);
if (hmod != NULL)
{
return hmod;
}
}
else if ((callingAssembly != nullptr) && searchAssemblyDirectory)
{
hmod = LoadFromPInvokeAssemblyDirectory(callingAssembly, currLibNameVariation, loadWithAlteredPathFlags | dllImportSearchPathFlags, pErrorTracker);
if (hmod != NULL)
{
return hmod;
}
}
hmod = LocalLoadLibraryHelper(currLibNameVariation, dllImportSearchPathFlags, pErrorTracker);
if (hmod != NULL)
{
return hmod;
}
}
// This may be an assembly name
// Format is "fileName, assemblyDisplayName"
MAKE_UTF8PTR_FROMWIDE(szLibName, wszLibName);
char *szComma = strchr(szLibName, ',');
if (szComma)
{
*szComma = '\0';
// Trim white spaces
while (COMCharacter::nativeIsWhiteSpace(*(++szComma)));
AssemblySpec spec;
if (SUCCEEDED(spec.Init(szComma)))
{
// Need to perform case insensitive hashing.
SString moduleName(SString::Utf8, szLibName);
moduleName.LowerCase();
StackScratchBuffer buffer;
szLibName = (LPSTR)moduleName.GetUTF8(buffer);
Assembly *pAssembly = spec.LoadAssembly(FILE_LOADED);
Module *pModule = pAssembly->FindModuleByName(szLibName);
hmod = LocalLoadLibraryHelper(pModule->GetPath(), loadWithAlteredPathFlags | dllImportSearchPathFlags, pErrorTracker);
}
}
return hmod;
}
NATIVE_LIBRARY_HANDLE LoadNativeLibraryBySearch(NDirectMethodDesc *pMD, LoadLibErrorTracker *pErrorTracker, PCWSTR wszLibName)
{
STANDARD_VM_CONTRACT;
BOOL searchAssemblyDirectory;
DWORD dllImportSearchPathFlags;
GetDllImportSearchPathFlags(pMD, &dllImportSearchPathFlags, &searchAssemblyDirectory);
Assembly *pAssembly = pMD->GetMethodTable()->GetAssembly();
return LoadNativeLibraryBySearch(pAssembly, searchAssemblyDirectory, dllImportSearchPathFlags, pErrorTracker, wszLibName);
}
}
// static
NATIVE_LIBRARY_HANDLE NativeLibrary::LoadLibraryByName(LPCWSTR libraryName, Assembly *callingAssembly,
BOOL hasDllImportSearchFlags, DWORD dllImportSearchFlags,
BOOL throwOnError)
{
CONTRACTL
{
STANDARD_VM_CHECK;
PRECONDITION(CheckPointer(libraryName));
PRECONDITION(CheckPointer(callingAssembly));
}
CONTRACTL_END;
NATIVE_LIBRARY_HANDLE hmod = nullptr;
// Resolve using the AssemblyLoadContext.LoadUnmanagedDll implementation
hmod = LoadNativeLibraryViaAssemblyLoadContext(callingAssembly, libraryName);
if (hmod != nullptr)
return hmod;
// Check if a default dllImportSearchPathFlags was passed in. If so, use that value.
// Otherwise, check if the assembly has the DefaultDllImportSearchPathsAttribute attribute.
// If so, use that value.
BOOL searchAssemblyDirectory;
DWORD dllImportSearchPathFlags;
if (hasDllImportSearchFlags)
{
dllImportSearchPathFlags = dllImportSearchFlags & ~DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY;
searchAssemblyDirectory = dllImportSearchFlags & DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY;
}
else
{
GetDllImportSearchPathFlags(callingAssembly->GetManifestModule(),
&dllImportSearchPathFlags, &searchAssemblyDirectory);
}
LoadLibErrorTracker errorTracker;
hmod = LoadNativeLibraryBySearch(callingAssembly, searchAssemblyDirectory, dllImportSearchPathFlags, &errorTracker, libraryName);
if (hmod != nullptr)
return hmod;
// Resolve using the AssemblyLoadContext.ResolvingUnmanagedDll event
hmod = LoadNativeLibraryViaAssemblyLoadContextEvent(callingAssembly, libraryName);
if (hmod != nullptr)
return hmod;
if (throwOnError)
{
SString libraryPathSString(libraryName);
errorTracker.Throw(libraryPathSString);
}
return hmod;
}
namespace
{
NATIVE_LIBRARY_HANDLE LoadNativeLibrary(NDirectMethodDesc * pMD, LoadLibErrorTracker * pErrorTracker)
{
CONTRACTL
{
STANDARD_VM_CHECK;
PRECONDITION( CheckPointer( pMD ) );
}
CONTRACTL_END;
LPCUTF8 name = pMD->GetLibName();
if ( !name || !*name )
return NULL;
PREFIX_ASSUME( name != NULL );
MAKE_WIDEPTR_FROMUTF8( wszLibName, name );
NativeLibraryHandleHolder hmod = LoadNativeLibraryViaDllImportResolver(pMD, wszLibName);
if (hmod != NULL)
{
return hmod.Extract();
}
AppDomain* pDomain = GetAppDomain();
Assembly* pAssembly = pMD->GetMethodTable()->GetAssembly();
hmod = LoadNativeLibraryViaAssemblyLoadContext(pAssembly, wszLibName);
if (hmod != NULL)
{
return hmod.Extract();
}
hmod = pDomain->FindUnmanagedImageInCache(wszLibName);
if (hmod != NULL)
{
return hmod.Extract();
}
hmod = LoadNativeLibraryBySearch(pMD, pErrorTracker, wszLibName);
if (hmod != NULL)
{
// If we have a handle add it to the cache.
pDomain->AddUnmanagedImageToCache(wszLibName, hmod);
return hmod.Extract();
}
hmod = LoadNativeLibraryViaAssemblyLoadContextEvent(pAssembly, wszLibName);
if (hmod != NULL)
{
return hmod.Extract();
}
return hmod.Extract();
}
}
NATIVE_LIBRARY_HANDLE NativeLibrary::LoadLibraryFromMethodDesc(NDirectMethodDesc * pMD)
{
CONTRACT(NATIVE_LIBRARY_HANDLE)
{
STANDARD_VM_CHECK;
PRECONDITION(CheckPointer(pMD));
POSTCONDITION(RETVAL != NULL);
}
CONTRACT_END;
LoadLibErrorTracker errorTracker;
NATIVE_LIBRARY_HANDLE hmod = LoadNativeLibrary(pMD, &errorTracker);
if (hmod == NULL)
{
if (pMD->GetLibName() == NULL)
COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDRESS_NONAME);
StackSString ssLibName(SString::Utf8, pMD->GetLibName());
errorTracker.Throw(ssLibName);
}
RETURN hmod;
}