Skip to content

Commit 22fc451

Browse files
authored
[Android] Add callback to host-runtime contract for getting assembly data (#112705)
Add a new callback to `host_runtime_contract` that is called default assembly resolution: ```c++ // Probe the host for `path`. Sets pointer to data start and its size, if found. // Returns true if found, false otherwise. If false, out parameter values are ignored by the runtime. bool(HOST_CONTRACT_CALLTYPE* external_assembly_probe)( const char* path, /*out*/ void** data /*out*/ int64_t* size); ``` Contributes to #112706 This is a set of minimal changes required to make dotnet/android#9572 (the `.NET for Android` CoreCLR host) work with standard .NET for Android applications.
1 parent d343214 commit 22fc451

File tree

16 files changed

+130
-70
lines changed

16 files changed

+130
-70
lines changed

eng/native/build-commons.sh

+14-11
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,20 @@ build_native()
6363
# All set to commence the build
6464
echo "Commencing build of \"$target\" target in \"$message\" for $__TargetOS.$__TargetArch.$__BuildType in $intermediatesDir"
6565

66+
SAVED_CFLAGS="${CFLAGS}"
67+
SAVED_CXXFLAGS="${CXXFLAGS}"
68+
SAVED_LDFLAGS="${LDFLAGS}"
69+
70+
# Let users provide additional compiler/linker flags via EXTRA_CFLAGS/EXTRA_CXXFLAGS/EXTRA_LDFLAGS.
71+
# If users directly override CFLAG/CXXFLAGS/LDFLAGS, that may lead to some configure tests working incorrectly.
72+
# See https://github.com/dotnet/runtime/issues/35727 for more information.
73+
#
74+
# These flags MUST be exported before gen-buildsys.sh runs or cmake will ignore them
75+
#
76+
export CFLAGS="${CFLAGS} ${EXTRA_CFLAGS}"
77+
export CXXFLAGS="${CXXFLAGS} ${EXTRA_CXXFLAGS}"
78+
export LDFLAGS="${LDFLAGS} ${EXTRA_LDFLAGS}"
79+
6680
if [[ "$targetOS" == osx || "$targetOS" == maccatalyst ]]; then
6781
if [[ "$hostArch" == x64 ]]; then
6882
cmakeArgs="-DCMAKE_OSX_ARCHITECTURES=\"x86_64\" $cmakeArgs"
@@ -194,17 +208,6 @@ build_native()
194208
return
195209
fi
196210

197-
SAVED_CFLAGS="${CFLAGS}"
198-
SAVED_CXXFLAGS="${CXXFLAGS}"
199-
SAVED_LDFLAGS="${LDFLAGS}"
200-
201-
# Let users provide additional compiler/linker flags via EXTRA_CFLAGS/EXTRA_CXXFLAGS/EXTRA_LDFLAGS.
202-
# If users directly override CFLAG/CXXFLAGS/LDFLAGS, that may lead to some configure tests working incorrectly.
203-
# See https://github.com/dotnet/runtime/issues/35727 for more information.
204-
export CFLAGS="${CFLAGS} ${EXTRA_CFLAGS}"
205-
export CXXFLAGS="${CXXFLAGS} ${EXTRA_CXXFLAGS}"
206-
export LDFLAGS="${LDFLAGS} ${EXTRA_LDFLAGS}"
207-
208211
local exit_code
209212
if [[ "$__StaticAnalyzer" == 1 ]]; then
210213
pushd "$intermediatesDir"

eng/native/gen-buildsys.sh

+4-10
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,9 @@ if [[ "$host_arch" == "wasm" ]]; then
106106
fi
107107
fi
108108

109-
$cmake_command \
110-
--no-warn-unused-cli \
111-
-G "$generator" \
112-
"-DCMAKE_BUILD_TYPE=$buildtype" \
113-
"-DCMAKE_INSTALL_PREFIX=$__CMakeBinDir" \
114-
$cmake_extra_defines \
115-
$__UnprocessedCMakeArgs \
116-
"${cmake_extra_defines_wasm[@]}" \
117-
-S "$1" \
118-
-B "$2"
109+
buildsys_command="$cmake_command --no-warn-unused-cli -G \"$generator\" \"-DCMAKE_BUILD_TYPE=$buildtype\" \"-DCMAKE_INSTALL_PREFIX=$__CMakeBinDir\" $cmake_extra_defines $__UnprocessedCMakeArgs \"${cmake_extra_defines_wasm[@]}\" -S \"$1\" -B \"$2\""
110+
buildsys_command=$(echo $buildsys_command | sed 's/""//g')
111+
echo $buildsys_command
112+
eval $buildsys_command
119113

120114
# don't add anything after this line so the cmake exit code gets propagated correctly

src/coreclr/dlls/mscoree/exports.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -292,9 +292,10 @@ int coreclr_initialize(
292292

293293
ConstWStringHolder appDomainFriendlyNameW = StringToUnicode(appDomainFriendlyName);
294294

295-
if (bundleProbe != nullptr)
295+
ExternalAssemblyProbeFn* externalAssemblyProbe = hostContract != nullptr ? hostContract->external_assembly_probe : nullptr;
296+
if (bundleProbe != nullptr || externalAssemblyProbe != nullptr)
296297
{
297-
static Bundle bundle(exePath, bundleProbe);
298+
static Bundle bundle(exePath, bundleProbe, externalAssemblyProbe);
298299
Bundle::AppBundle = &bundle;
299300
}
300301

src/coreclr/hosts/inc/coreclrhost.h

+1
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ CORECLR_HOSTING_API(coreclr_execute_assembly,
150150
//
151151
// Callback types used by the hosts
152152
//
153+
typedef bool(CORECLR_CALLING_CONVENTION ExternalAssemblyProbeFn)(const char* path, void** data_start, int64_t* size);
153154
typedef bool(CORECLR_CALLING_CONVENTION BundleProbeFn)(const char* path, int64_t* offset, int64_t* size, int64_t* compressedSize);
154155
typedef const void* (CORECLR_CALLING_CONVENTION PInvokeOverrideFn)(const char* libraryName, const char* entrypointName);
155156

src/coreclr/inc/bundle.h

+8-6
Original file line numberDiff line numberDiff line change
@@ -18,29 +18,31 @@ class Bundle;
1818
struct BundleFileLocation
1919
{
2020
INT64 Size;
21+
void* DataStart;
2122
INT64 Offset;
2223
INT64 UncompresedSize;
2324

2425
BundleFileLocation()
25-
{
26+
{
2627
LIMITED_METHOD_CONTRACT;
2728

2829
Size = 0;
29-
Offset = 0;
30+
DataStart = nullptr;
31+
Offset = 0;
3032
UncompresedSize = 0;
3133
}
3234

3335
static BundleFileLocation Invalid() { LIMITED_METHOD_CONTRACT; return BundleFileLocation(); }
3436

3537
const SString &Path() const;
3638

37-
bool IsValid() const { LIMITED_METHOD_CONTRACT; return Offset != 0; }
39+
bool IsValid() const { LIMITED_METHOD_CONTRACT; return DataStart != nullptr || Offset != 0; }
3840
};
3941

4042
class Bundle
4143
{
4244
public:
43-
Bundle(LPCSTR bundlePath, BundleProbeFn *probe);
45+
Bundle(LPCSTR bundlePath, BundleProbeFn *probe, ExternalAssemblyProbeFn* externalAssemblyProbe = nullptr);
4446
BundleFileLocation Probe(const SString& path, bool pathIsBundleRelative = false) const;
4547

4648
const SString &Path() const { LIMITED_METHOD_CONTRACT; return m_path; }
@@ -51,9 +53,9 @@ class Bundle
5153
static BundleFileLocation ProbeAppBundle(const SString& path, bool pathIsBundleRelative = false);
5254

5355
private:
54-
55-
SString m_path; // The path to single-file executable
56+
SString m_path; // The path to single-file executable or package name/id on Android
5657
BundleProbeFn *m_probe;
58+
ExternalAssemblyProbeFn *m_externalAssemblyProbe;
5759

5860
SString m_basePath; // The prefix to denote a path within the bundle
5961
COUNT_T m_basePathLength;

src/coreclr/pal/src/CMakeLists.txt

+1-2
Original file line numberDiff line numberDiff line change
@@ -300,8 +300,7 @@ if(CLR_CMAKE_TARGET_LINUX)
300300
else(NOT CLR_CMAKE_TARGET_ANDROID)
301301
target_link_libraries(coreclrpal
302302
PUBLIC
303-
${ANDROID_GLOB}
304-
${LZMA})
303+
${ANDROID_GLOB})
305304
endif(NOT CLR_CMAKE_TARGET_ANDROID)
306305

307306
target_link_libraries(coreclrpal

src/coreclr/vm/bundle.cpp

+38-19
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,18 @@ const SString &BundleFileLocation::Path() const
3030
return Bundle::AppBundle->Path();
3131
}
3232

33-
Bundle::Bundle(LPCSTR bundlePath, BundleProbeFn *probe)
33+
Bundle::Bundle(LPCSTR bundlePath, BundleProbeFn *probe, ExternalAssemblyProbeFn* externalAssemblyProbe)
34+
: m_probe(probe)
35+
, m_externalAssemblyProbe(externalAssemblyProbe)
36+
, m_basePathLength(0)
3437
{
3538
STANDARD_VM_CONTRACT;
3639

37-
_ASSERTE(probe != nullptr);
40+
_ASSERTE(m_probe != nullptr || m_externalAssemblyProbe != nullptr);
3841

42+
// On Android this is not a real path, but rather the application's package name
3943
m_path.SetUTF8(bundlePath);
40-
m_probe = probe;
41-
44+
#if !defined(TARGET_ANDROID)
4245
// The bundle-base path is the directory containing the single-file bundle.
4346
// When the Probe() function searches within the bundle, it masks out the basePath from the assembly-path (if found).
4447

@@ -47,14 +50,13 @@ Bundle::Bundle(LPCSTR bundlePath, BundleProbeFn *probe)
4750
size_t baseLen = pos - bundlePath + 1; // Include DIRECTORY_SEPARATOR_CHAR_A in m_basePath
4851
m_basePath.SetUTF8(bundlePath, (COUNT_T)baseLen);
4952
m_basePathLength = (COUNT_T)baseLen;
53+
#endif // !TARGET_ANDROID
5054
}
5155

5256
BundleFileLocation Bundle::Probe(const SString& path, bool pathIsBundleRelative) const
5357
{
5458
STANDARD_VM_CONTRACT;
5559

56-
BundleFileLocation loc;
57-
5860
// Skip over m_base_path, if any. For example:
5961
// Bundle.Probe("lib.dll") => m_probe("lib.dll")
6062
// Bundle.Probe("path/to/exe/lib.dll") => m_probe("lib.dll")
@@ -77,27 +79,44 @@ BundleFileLocation Bundle::Probe(const SString& path, bool pathIsBundleRelative)
7779
else
7880
{
7981
// This is not a file within the bundle
80-
return loc;
82+
return BundleFileLocation::Invalid();
8183
}
8284
}
8385

84-
INT64 fileSize = 0;
85-
INT64 compressedSize = 0;
86-
87-
m_probe(utf8Path, &loc.Offset, &fileSize, &compressedSize);
88-
89-
if (compressedSize)
86+
if (m_probe != nullptr)
9087
{
91-
loc.Size = compressedSize;
92-
loc.UncompresedSize = fileSize;
88+
BundleFileLocation loc;
89+
INT64 fileSize = 0;
90+
INT64 compressedSize = 0;
91+
if (m_probe(utf8Path, &loc.Offset, &fileSize, &compressedSize))
92+
{
93+
// Found assembly in bundle
94+
if (compressedSize)
95+
{
96+
loc.Size = compressedSize;
97+
loc.UncompresedSize = fileSize;
98+
}
99+
else
100+
{
101+
loc.Size = fileSize;
102+
loc.UncompresedSize = 0;
103+
}
104+
105+
return loc;
106+
}
93107
}
94-
else
108+
109+
if (m_externalAssemblyProbe != nullptr)
95110
{
96-
loc.Size = fileSize;
97-
loc.UncompresedSize = 0;
111+
BundleFileLocation loc;
112+
if (m_externalAssemblyProbe(utf8Path, &loc.DataStart, &loc.Size))
113+
{
114+
// Found via external assembly probe
115+
return loc;
116+
}
98117
}
99118

100-
return loc;
119+
return BundleFileLocation::Invalid();
101120
}
102121

103122
BundleFileLocation Bundle::ProbeAppBundle(const SString& path, bool pathIsBundleRelative)

src/coreclr/vm/coreassemblyspec.cpp

+6-3
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,13 @@ STDAPI BinderAcquirePEImage(LPCWSTR wszAssemblyPath,
8989
PEImageHolder pImage = PEImage::OpenImage(wszAssemblyPath, MDInternalImport_Default, bundleFileLocation);
9090

9191
// Make sure that the IL image can be opened.
92-
hr=pImage->TryOpenFile();
93-
if (FAILED(hr))
92+
if (pImage->IsFile())
9493
{
95-
goto Exit;
94+
hr = pImage->TryOpenFile();
95+
if (FAILED(hr))
96+
{
97+
goto Exit;
98+
}
9699
}
97100

98101
if (pImage)

src/coreclr/vm/peimage.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -863,13 +863,15 @@ HRESULT PEImage::TryOpenFile(bool takeLock)
863863
{
864864
STANDARD_VM_CONTRACT;
865865

866+
_ASSERTE(IsFile());
867+
866868
SimpleWriteLockHolder lock(m_pLayoutLock, takeLock);
867869

868-
if (m_hFile!=INVALID_HANDLE_VALUE)
870+
if (m_hFile != INVALID_HANDLE_VALUE)
869871
return S_OK;
870872

871873
ErrorModeHolder mode{};
872-
m_hFile=WszCreateFile((LPCWSTR)GetPathToLoad(),
874+
m_hFile = WszCreateFile((LPCWSTR)GetPathToLoad(),
873875
GENERIC_READ
874876
#if TARGET_WINDOWS
875877
// the file may have native code sections, make sure we are allowed to execute the file
@@ -881,7 +883,6 @@ HRESULT PEImage::TryOpenFile(bool takeLock)
881883
OPEN_EXISTING,
882884
FILE_ATTRIBUTE_NORMAL,
883885
NULL);
884-
885886
if (m_hFile != INVALID_HANDLE_VALUE)
886887
return S_OK;
887888

src/coreclr/vm/peimage.h

+1
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ class PEImage final
138138

139139
BOOL IsFile();
140140
BOOL IsInBundle() const;
141+
void* GetExternalData(INT64* size);
141142
INT64 GetOffset() const;
142143
INT64 GetSize() const;
143144
INT64 GetUncompressedSize() const;

src/coreclr/vm/peimage.inl

+11-2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,16 @@ inline const SString& PEImage::GetPathToLoad()
3939
return IsInBundle() ? m_bundleFileLocation.Path() : m_path;
4040
}
4141

42+
inline void* PEImage::GetExternalData(INT64* size)
43+
{
44+
LIMITED_METHOD_CONTRACT;
45+
46+
_ASSERTE(size != nullptr);
47+
48+
*size = m_bundleFileLocation.Size;
49+
return m_bundleFileLocation.DataStart;
50+
}
51+
4252
inline INT64 PEImage::GetOffset() const
4353
{
4454
LIMITED_METHOD_CONTRACT;
@@ -98,8 +108,7 @@ inline const SString &PEImage::GetModuleFileNameHintForDAC()
98108
inline BOOL PEImage::IsFile()
99109
{
100110
WRAPPER_NO_CONTRACT;
101-
102-
return !GetPathToLoad().IsEmpty();
111+
return m_bundleFileLocation.DataStart == nullptr && !GetPathToLoad().IsEmpty();
103112
}
104113

105114
//

src/coreclr/vm/peimagelayout.cpp

+16-5
Original file line numberDiff line numberDiff line change
@@ -616,17 +616,28 @@ FlatImageLayout::FlatImageLayout(PEImage* pOwner)
616616
PRECONDITION(CheckPointer(pOwner));
617617
}
618618
CONTRACTL_END;
619-
m_pOwner=pOwner;
620-
621-
HANDLE hFile = pOwner->GetFileHandle();
622-
INT64 offset = pOwner->GetOffset();
623-
INT64 size = pOwner->GetSize();
619+
m_pOwner = pOwner;
624620

625621
#ifdef LOGGING
626622
SString ownerPath{ pOwner->GetPath() };
627623
LOG((LF_LOADER, LL_INFO100, "PEImage: Opening flat %s\n", ownerPath.GetUTF8()));
628624
#endif // LOGGING
629625

626+
INT64 dataSize;
627+
void* data = pOwner->GetExternalData(&dataSize);
628+
if (data != nullptr)
629+
{
630+
// Image was provided as flat data via external assembly probing.
631+
// We do not manage the data - just initialize with it directly.
632+
_ASSERTE(dataSize != 0);
633+
Init(data, (COUNT_T)dataSize);
634+
return;
635+
}
636+
637+
HANDLE hFile = pOwner->GetFileHandle();
638+
INT64 offset = pOwner->GetOffset();
639+
INT64 size = pOwner->GetSize();
640+
630641
// If a size is not specified, load the whole file
631642
if (size == 0)
632643
{

src/coreclr/vm/peimagelayout.h

+6-4
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,7 @@ class FlatImageLayout : public PEImageLayout
8181
{
8282
VPTR_VTABLE_CLASS(FlatImageLayout, PEImageLayout)
8383
VPTR_UNIQUE(0x59)
84-
protected:
85-
CLRMapViewHolder m_FileView;
8684
public:
87-
HandleHolder m_FileMap;
88-
8985
#ifndef DACCESS_COMPILE
9086
FlatImageLayout(PEImage* pOwner);
9187
FlatImageLayout(PEImage* pOwner, const BYTE* array, COUNT_T size);
@@ -94,6 +90,12 @@ class FlatImageLayout : public PEImageLayout
9490
void* LoadImageByMappingParts(SIZE_T* m_imageParts) const;
9591
#endif
9692
#endif
93+
94+
private:
95+
// Handles for the mapped image.
96+
// These will be null if the image data is not mapped by the runtime (for example, provided via an external assembly probe).
97+
CLRMapViewHolder m_FileView;
98+
HandleHolder m_FileMap;
9799
};
98100

99101
// ConvertedImageView is for the case when we construct a loaded

src/coreclr/vm/util.cpp

+8-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515

1616
#ifndef DACCESS_COMPILE
1717

18+
#if defined(TARGET_ANDROID)
19+
#include <android/log.h>
20+
#endif // defined(TARGET_ANDROID)
1821

1922
thread_local size_t t_ThreadType;
2023

@@ -177,10 +180,13 @@ void PrintToStdErrA(const char *pszString) {
177180
}
178181
CONTRACTL_END
179182

180-
HANDLE Handle = GetStdHandle(STD_ERROR_HANDLE);
181-
183+
#if defined(TARGET_ANDROID)
184+
__android_log_write(ANDROID_LOG_FATAL, MAIN_CLR_MODULE_NAME_A, pszString);
185+
#else
186+
HANDLE Handle = GetStdHandle(STD_ERROR_HANDLE);
182187
size_t len = strlen(pszString);
183188
NPrintToHandleA(Handle, pszString, len);
189+
#endif // defined(TARGET_ANDROID)
184190
}
185191

186192
void PrintToStdErrW(const WCHAR *pwzString)

0 commit comments

Comments
 (0)