Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Android] Add callback to host-runtime contract for getting assembly data #112705

Merged
merged 69 commits into from
Feb 25, 2025

Conversation

grendello
Copy link
Contributor

@grendello grendello commented Feb 19, 2025

Add a new callback to host_runtime_contract that is called default assembly resolution:

// 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 also makes fatal errors that the runtime prints to stderr get printed to logcat on Android.

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.

@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Feb 19, 2025
Copy link
Contributor

Tagging subscribers to this area: @mangod9
See info in area-owners.md if you want to be subscribed.

@lewing
Copy link
Member

lewing commented Feb 19, 2025

/azp run runtime

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@AaronRobinsonMSFT
Copy link
Member

@grendello I think a bunch of these changes make sense to me, for example, a bunch of the bundling logic. My pushback would be either we are doing something quick and dirty or we are chasing optimizations, not both. For example coreclr_initialize seems to be just fine, but we don't want to have the other side convert to UTF-8, why not? Would that make composition harder in some way? That is the existing API and we should keep that for now to get things up.

I would change the android side to conform, where possible, to existing APIs for now and put a bunch of the cost there.

@jkotas
Copy link
Member

jkotas commented Feb 19, 2025

This change was incorrectly merged with main and a lot of unrelated commits from main are showing up as part of the delta. Could you please rebase against main and force push so that the delta is reviewable?

@grendello
Copy link
Contributor Author

This change was incorrectly merged with main and a lot of unrelated commits from main are showing up as part of the delta. Could you please rebase against main and force push so that the delta is reviewable?

I use git merge, which is the correct way of tracking changes, because it doesn't rewrite history like git rebase does, but sure, I can rebase.

@grendello
Copy link
Contributor Author

@grendello I think a bunch of these changes make sense to me, for example, a bunch of the bundling logic. My pushback would be either we are doing something quick and dirty or we are chasing optimizations, not both. For example coreclr_initialize seems to be just fine, but we don't want to have the other side convert to UTF-8, why not? Would that make composition harder in some way? That is the existing API and we should keep that for now to get things up.

Using UTF-8 strings on the Android side is easy and bears no costs for us, I can make the change without any effort. This is not the only issue with coreclr_initialize, though, and I'm going to push back against using the pointer-to-string-to-pointer conversion (as I did years ago when the same approach was suggested for MonoVM).

I would change the android side to conform, where possible, to existing APIs for now and put a bunch of the cost there.

👍

@grendello grendello force-pushed the dev/grendel/android-clr-host-local branch 2 times, most recently from ff6a948 to b285800 Compare February 20, 2025 10:02
@steveisok steveisok marked this pull request as ready for review February 24, 2025 15:57
Copy link
Member

@ivanpovazan ivanpovazan Feb 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we have changes in hosting header files (this one and src/native/corehost/host_runtime_contract.h below), I think we could consider shipping hosting headers as part of the Android CoreCLR runtime pack?

@elinor-fung @jkotas what do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have been treating the coreclr.dll hosting interface as an internal detail that is not officially documented and that is not guaranteed to be stable. Are you suggesting that we change that?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we ship headers and still consider them not guaranteed to be stable? That's what mono does with certain parts of its hosting API.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition to what Steve said:

Currently, on dotnet/macios side the build downloads the coreclrhost.h header file to build the CoreCLR host:
https://github.com/dotnet/macios/blob/30cce24d4c18d149879fe8afa64331491535ea0b/runtime/Makefile#L38
so with every change to the file, the download URL needs to be updated to have consistent behavior. This is difficult to maintain as the version of the file needs to be aligned with the runtime pack version including the actual implementation changes.
As we are now bringing CoreCLR support for more platforms and as we are in the development stage syncing changes between the runtime and embedders/hosts, I was curious if we would want to ship the required header files as part of the runtime pack (like mono does), to avoid manual downloads or potential mismatches.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's always the option of sharing a transport package if we don't want to ship it.

Copy link
Member

@elinor-fung elinor-fung Feb 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we want the internal coreclr interface as part of the shipped runtime pack. We do include public hosting headers (coreclr_delegates.h, hostfxr.h, nethost.h) in the host pack.

Transport package seems reasonable if we need a better way for other repos to get at internals.

@ivanpovazan
Copy link
Member

ivanpovazan commented Feb 24, 2025

@elinor-fung it seems like we are stripping this PR down to what is described in: #112706 should we rename the PR and link it to that issue, so that it becomes in-pr ?

Copy link
Member

@jkotas jkotas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change LGTM once we get a successful CI run.

Copy link
Member

@janvorli janvorli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thank you!

@elinor-fung elinor-fung changed the title [Android] Minimal (and quick) set of changes to make CoreCLR host on Android work [Android] Add callback to host-runtime contract for getting assembly data Feb 24, 2025
@elinor-fung
Copy link
Member

Follow-ups I plan on for #112706:

  • host tests for external_assembly_probe
  • refactoring/renaming to separate external assembly probe from bundle / bundle file location idea
    • external assembly probe doesn't have to mean bundle - plugging into the bundle probing was convenient, since it was already hooked in to be called before all the default resolution, but from the runtime side, external assembly data from the host shouldn't equate to bundle
    • BundleFileLocation name seems misleading now that it also represents externally provided assembly data

@steveisok
Copy link
Member

Thanks everyone for working together and helping to see this through!

@steveisok steveisok merged commit 22fc451 into dotnet:main Feb 25, 2025
136 of 149 checks passed
@grendello grendello deleted the dev/grendel/android-clr-host-local branch February 25, 2025 12:04
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these changes may break source-build, as it effectively regresses #35727. cc: @omajid can you take a look?

@grendello What was the motivation behind this change?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is expanding the coverage of #35727 to cmake configure step. How is it regressing, do you have an example?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From #35727:

The normal way to do that is by exporting environment variables like CFLAGS and CXXFLAGS. Fedora provides a bunch of values that we can use . But this doesn't play very nice with the liberal use of -Werror in corefx. Worse, the flags leak into various configure tests . For example HAVE_IN_PKTINFO would fail due to an unrelated warning

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jkoritzinsky *FLAGS must be exported before cmake configuration run, otherwise they will be ignored. Where they were previously exported, they were essentially ignored. My motivation, tbh, was to get color in errors and warnings working, but it just seemed to be a mistake to have the exports without any effect. I think it may also be a requirement for packagers in some Linux distributions. For instance, Debian can specify standard system-wide package compilation flags via various dpkg scripts and it's done by pre-setting the *FLAGS environment variables.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it just seemed to be a mistake to have the exports without any effect.

Before this PR extra flags did had effect but only over cmake --build step, not the config step.

If something is breaking because of this, it is most likely the problem which needs to be fixed in that config.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@omajid The policies talk about build, but they don't mean cmake build stage. cmake is a build system generator, so it takes the *FLAGS into account only when generating the make/ninja build files, during cmake configuration phase. Here's dotnet/runtime's build.ninja:

artifacts/obj/coreclr/android.arm64.Release $ grep -e '\(CXX\|LD\|CC\)FLAGS' build.ninja | wc -l
0

artifacts/obj/coreclr/android.arm64.Release $ grep -e 'FLAGS' build.ninja | wc -l
2833

artifacts/obj/coreclr/android.arm64.Release $ grep -e 'FLAGS' build.ninja | head -1
  FLAGS = -g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -fno-rtti  -O3 -DNDEBUG  -std=gnu++11 -fPIE -O2 -Wall -Wno-null-conversion -glldb -fno-omit-frame-pointer -fwrapv -fstack-protector-strong -Werror -Wno-unused-variable -Wno-unused-value -Wno-unused-function -Wno-tautological-compare -Wno-unknown-pragmas -Wimplicit-fallthrough -Wvla -Wno-invalid-offsetof -Wno-unused-but-set-variable -ffp-contract=off -fno-rtti -Wno-unknown-warning-option -ferror-limit=4096 -Wno-unused-private-field -Wno-constant-logical-operand -Wno-pragma-pack -Wno-incompatible-ms-struct -Wno-reserved-identifier -Wno-unsafe-buffer-usage -Wno-single-bit-bitfield-constant-conversion -Wno-cast-function-type-strict -Wno-switch-default -Wno-nontrivial-memaccess -fsigned-char -fvisibility=hidden -ffunction-sections

As you can see, there's no mention of any of the *FLAGS - all of them were expanded and placed by cmake configuration stage in the build file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@omajid if setting some flags at configure time breaks .NET tests, then the problem is in those tests and it should be fixed there. If any component is incompatible with some flags, it can (and should) check for and remove the offending flags/defines. This post gives a potential solution (not tested by me):

function(remove_compiler_flag target flag)
  foreach(property IN COMPILE_DEFINITIONS COMPILE_FEATURES COMPILE_FLAGS COMPILE_OPTIONS etc)
    # Get the target property
    # If the specified flag is in that list, remove it and re-set the target property
  endforeach()
endfunction()

Copy link
Member

@am11 am11 Feb 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@omajid, somehow it stopped working. The cmake generated makefiles (or ninja files) do not explicitly account for $CFLAGS etc. from environment variable anymore (assuming it was working when the original issue was fixed):

FROM alpine:latest

RUN apk add --no-cache cmake make ninja gcc musl-dev

WORKDIR /app

RUN cat <<'EOF' > CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(HelloWorld C)

add_executable(hello main.c)
EOF

RUN cat <<'EOF' > main.c
#include <stdio.h>

int main() {
    #ifdef PRE_CONFIG_FLAG
        #error pre config CFLAGS were respected!
    #else
        #error pre config CFLAGS were ignored!
    #endif

    #ifdef POST_CONFIG_FLAG
        #error post config CFLAGS were respected!
    #else
        #error post config CFLAGS were ignored!
    #endif

    return 0;
}
EOF

ENV CFLAGS="-Wall -Wextra -DPRE_CONFIG_FLAG"

RUN cmake -G Ninja -B build_ninja
RUN cmake -G "Unix Makefiles" -B build_make

ENV CFLAGS="-Wall -Wextra -DPOST_CONFIG_FLAG"

RUN ninja -C build_ninja -v || true

RUN make -C build_make VERBOSE=1

with docker build . -t foo 2>&1 | tee
both ninja and make (penultimate and last lines) show us:

#12 0.125 /app/main.c:5:10: error: #error pre config CFLAGS were respected!
#12 0.125     5 |         #error pre config CFLAGS were respected!
#12 0.125       |          ^~~~~
#12 0.125 /app/main.c:13:10: error: #error post config CFLAGS were ignored!
#12 0.125    13 |         #error post config CFLAGS were ignored!
#12 0.125       |          ^~~~~

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we revert the part of this change that impacts source build? Or are we ok with fixing outside of runtime?

Copy link
Member

@omajid omajid Feb 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @am11 and @grendello !

@steveisok said:

Or are we ok with fixing outside of runtime?

Based on the comments and feedback, I think we should fix it as a separate PR. We don't know what that exactly looks like, right now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.