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

Mono musl support #76500

Merged
merged 4 commits into from
Jan 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@
<_runtimeOS Condition="'$(TargetsMobile)' == 'true'">$(TargetOS.ToLowerInvariant())</_runtimeOS>

<_portableOS>linux</_portableOS>
<_portableOS Condition="'$(_runtimeOS)' == 'linux-musl'">linux-musl</_portableOS>
<_portableOS Condition="'$(_runtimeOS)' == 'linux-musl' or $(_runtimeOS.StartsWith('alpine'))">linux-musl</_portableOS>
<_portableOS Condition="'$(_runtimeOS)' == 'linux-bionic'">linux-bionic</_portableOS>
<_portableOS Condition="'$(_hostOS)' == 'osx'">osx</_portableOS>
<_portableOS Condition="'$(_runtimeOS)' == 'win' or '$(TargetOS)' == 'windows'">win</_portableOS>
Expand Down Expand Up @@ -219,6 +219,7 @@
<OutputRid Condition="'$(OutputRid)' == ''">$(PackageRID)</OutputRid>
<OutputRid Condition="'$(PortableBuild)' == 'true'">$(_portableOS)-$(TargetArchitecture)</OutputRid>
<TargetsLinuxBionic Condition="$(OutputRid.StartsWith('linux-bionic'))">true</TargetsLinuxBionic>
<TargetsLinuxMusl Condition="'$(_portableOS)' == 'linux-musl'">true</TargetsLinuxMusl>
</PropertyGroup>

<PropertyGroup Label="CalculateTargetOSName" Condition="'$(SkipInferTargetOSName)' != 'true'">
Expand Down
6 changes: 5 additions & 1 deletion eng/native/tryrun.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ endmacro()

if(EXISTS ${CROSS_ROOTFS}/usr/lib/gcc/armv7-alpine-linux-musleabihf OR
EXISTS ${CROSS_ROOTFS}/usr/lib/gcc/armv6-alpine-linux-musleabihf OR
EXISTS ${CROSS_ROOTFS}/usr/lib/gcc/aarch64-alpine-linux-musl)
EXISTS ${CROSS_ROOTFS}/usr/lib/gcc/aarch64-alpine-linux-musl OR
EXISTS ${CROSS_ROOTFS}/usr/lib/gcc/s390x-alpine-linux-musl OR
EXISTS ${CROSS_ROOTFS}/usr/lib/gcc/ppc64le-alpine-linux-musl OR
EXISTS ${CROSS_ROOTFS}/usr/lib/gcc/i586-alpine-linux-musl OR
EXISTS ${CROSS_ROOTFS}/usr/lib/gcc/riscv64-alpine-linux-musl)

set(ALPINE_LINUX 1)
elseif(EXISTS ${CROSS_ROOTFS}/bin/freebsd-version)
Expand Down
5 changes: 5 additions & 0 deletions src/coreclr/pal/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,11 @@ if(CLR_CMAKE_TARGET_LINUX)
target_link_libraries(coreclrpal ${UNWIND_LIBS})
endif(CLR_CMAKE_USE_SYSTEM_LIBUNWIND)

# bundled libunwind requires using libucontext on alpine and x86 and ppc64le
if(CLR_CMAKE_TARGET_ALPINE_LINUX AND (CLR_CMAKE_TARGET_ARCH_I386 OR CLR_CMAKE_TARGET_ARCH_POWERPC64))
target_link_libraries(coreclrpal ucontext)
endif(CLR_CMAKE_TARGET_ALPINE_LINUX AND (CLR_CMAKE_TARGET_ARCH_I386 OR CLR_CMAKE_TARGET_ARCH_POWERPC64))

endif(CLR_CMAKE_TARGET_LINUX)

if(CLR_CMAKE_TARGET_NETBSD)
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/pal/src/misc/perfjitdump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
#include <sys/uio.h>
#include <time.h>
#include <unistd.h>
#include <linux/limits.h>
#include <limits.h>

#include "../inc/llvm/ELF.h"

Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/vm/i386/cgenx86.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1133,6 +1133,7 @@ extern "C" DWORD __stdcall avx512StateSupport()

#else // !TARGET_UNIX

#if !__has_builtin(__cpuid)
void __cpuid(int cpuInfo[4], int function_id)
{
// Based on the Clang implementation provided in cpuid.h:
Expand All @@ -1143,7 +1144,9 @@ void __cpuid(int cpuInfo[4], int function_id)
: "0"(function_id)
);
}
#endif

#if !__has_builtin(__cpuidex)
void __cpuidex(int cpuInfo[4], int function_id, int subFunction_id)
{
// Based on the Clang implementation provided in cpuid.h:
Expand All @@ -1154,6 +1157,7 @@ void __cpuidex(int cpuInfo[4], int function_id, int subFunction_id)
: "0"(function_id), "2"(subFunction_id)
);
}
#endif

extern "C" DWORD __stdcall xmmYmmStateSupport()
{
Expand Down
42 changes: 39 additions & 3 deletions src/mono/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,36 @@ elseif(CLR_CMAKE_HOST_OS STREQUAL "linux")
# Enable the "full RELRO" options (RELRO & BIND_NOW) at link time
add_link_options(-Wl,-z,relro)
add_link_options(-Wl,-z,now)

# Detect Linux ID
# TODO: Eventually merge with eng/native/configureplatform.cmake
set(LINUX_ID_FILE "/etc/os-release")
if(CMAKE_CROSSCOMPILING)
set(LINUX_ID_FILE "${CMAKE_SYSROOT}${LINUX_ID_FILE}")
endif()

if(EXISTS ${LINUX_ID_FILE})
execute_process(
COMMAND bash -c "source ${LINUX_ID_FILE} && echo \$ID"
OUTPUT_VARIABLE CLR_CMAKE_LINUX_ID
OUTPUT_STRIP_TRAILING_WHITESPACE)

execute_process(
COMMAND bash -c "if strings \"${CMAKE_SYSROOT}/usr/bin/ldd\" 2>&1 | grep -q musl; then echo musl; fi"
OUTPUT_VARIABLE CLR_CMAKE_LINUX_MUSL
OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()

if(DEFINED CLR_CMAKE_LINUX_ID)
if(CLR_CMAKE_LINUX_ID STREQUAL alpine)
set(CLR_CMAKE_HOST_ALPINE_LINUX 1)
set(CLR_CMAKE_HOST_OS ${CLR_CMAKE_LINUX_ID})
endif()

if(CLR_CMAKE_LINUX_MUSL STREQUAL musl)
set(CLR_CMAKE_HOST_LINUX_MUSL 1)
endif()
endif(DEFINED CLR_CMAKE_LINUX_ID)
elseif(CLR_CMAKE_HOST_OS STREQUAL "android")
set(HOST_LINUX 1)
add_definitions(-D_GNU_SOURCE -D_REENTRANT)
Expand Down Expand Up @@ -309,7 +339,7 @@ elseif(TARGET_SYSTEM_NAME STREQUAL "ios" OR TARGET_SYSTEM_NAME STREQUAL "tvos")
set(TARGET_TVOS 1)
endif()
set(TARGET_APPLE_MOBILE 1)
elseif(TARGET_SYSTEM_NAME STREQUAL "linux")
elseif(TARGET_SYSTEM_NAME STREQUAL "linux" OR TARGET_SYSTEM_NAME STREQUAL "alpine")
set(TARGET_LINUX 1)
elseif(TARGET_SYSTEM_NAME STREQUAL "android")
set(TARGET_ANDROID 1)
Expand Down Expand Up @@ -592,10 +622,10 @@ if(LLVM_PREFIX)
set(llvm_cxxflags "-I${LLVM_PREFIX}/include -std=c++14 -fno-exceptions -fno-rtti -D__STDC_CONSTANT_MACROS -D__STD_FORMAT_MACROS -D__STDC_LIMIT_MACROS")
set(llvm_includedir "${LLVM_PREFIX}/include")

if(CLR_CMAKE_HOST_OS STREQUAL "linux")
if(HOST_LINUX)
# llvm-config --system-libs
set(llvm_system_libs "-lz" "-lrt" "-ldl" "-lpthread" "-lm")
elseif(CLR_CMAKE_HOST_OS STREQUAL "darwin")
elseif(HOST_OSX)
# llvm-config --system-libs
set(llvm_system_libs "-lz" "-lm")
endif()
Expand Down Expand Up @@ -876,6 +906,12 @@ if(HOST_IOS OR HOST_ANDROID OR HOST_MACCAT)
else()
set(DISABLE_DLLMAP 1)
endif()

if(CLR_CMAKE_HOST_ALPINE_LINUX)
# On Alpine Linux, we need to ensure that the reported stack range for the primary thread is
# larger than the initial committed stack size.
add_definitions(-DENSURE_PRIMARY_STACK_SIZE)
endif()
### End of OS specific checks

include_directories("${CLR_SRC_NATIVE_DIR}")
Expand Down
22 changes: 12 additions & 10 deletions src/mono/mono.proj
Original file line number Diff line number Diff line change
Expand Up @@ -633,19 +633,21 @@
<MonoToolchainPrebuiltOS Condition="$([MSBuild]::IsOSPlatform('OSX'))">darwin-x86_64</MonoToolchainPrebuiltOS>
<MonoToolchainPrebuiltOS Condition="'$(HostOS)' == 'windows'">windows-x86_64</MonoToolchainPrebuiltOS>
<_MonoRuntimeFilePath>$(MonoObjDir)out\lib\$(MonoFileName)</_MonoRuntimeFilePath>
<_LinuxAbi Condition="'$(TargetsAndroid)' != 'true' and '$(TargetsLinuxBionic)' != 'true'">gnu</_LinuxAbi>
<_LinuxAbi Condition="'$(TargetsAndroid)' == 'true' or '$(TargetsLinuxBionic)' == 'true'">android</_LinuxAbi>
<_LinuxAbi Condition="'$(TargetsAndroid)' != 'true' and '$(TargetsLinuxBionic)' != 'true' and '$(TargetsLinuxMusl)' != 'true'">linux-gnu</_LinuxAbi>
<_LinuxAbi Condition="'$(TargetsAndroid)' != 'true' and '$(TargetsLinuxBionic)' != 'true' and '$(TargetsLinuxMusl)' == 'true'">alpine-linux-musl</_LinuxAbi>
ayakael marked this conversation as resolved.
Show resolved Hide resolved
<_LinuxAbi Condition="'$(TargetsAndroid)' == 'true' or '$(TargetsLinuxBionic)' == 'true'">linux-android</_LinuxAbi>
<_LinuxFloatAbi Condition="'$(TargetsAndroid)' != 'true' and '$(TargetsLinuxBionic)' != 'true'">hf</_LinuxFloatAbi>
<_Objcopy>objcopy</_Objcopy>
<_Objcopy Condition="'$(Platform)' == 'arm'">arm-linux-$(_LinuxAbi)eabi$(_LinuxFloatAbi)-$(_Objcopy)</_Objcopy>
<_Objcopy Condition="'$(Platform)' == 'armv6'">arm-linux-$(_LinuxAbi)eabi$(_LinuxFloatAbi)-$(_Objcopy)</_Objcopy>
<_Objcopy Condition="'$(Platform)' == 'arm64'">aarch64-linux-$(_LinuxAbi)-$(_Objcopy)</_Objcopy>
<_Objcopy Condition="'$(Platform)' == 'riscv64'">riscv64-linux-$(_LinuxAbi)-$(_Objcopy)</_Objcopy>
<_Objcopy Condition="'$(Platform)' == 's390x'">s390x-linux-$(_LinuxAbi)-$(_Objcopy)</_Objcopy>
<_Objcopy Condition="'$(Platform)' == 'ppc64le'">powerpc64le-linux-$(_LinuxAbi)-$(_Objcopy)</_Objcopy>
<_Objcopy Condition="'$(Platform)' == 'x64'">x86_64-linux-$(_LinuxAbi)-$(_Objcopy)</_Objcopy>
<_Objcopy Condition="'$(Platform)' == 'x86'">i686-linux-$(_LinuxAbi)-$(_Objcopy)</_Objcopy>
<_Objcopy Condition="'$(Platform)' == 'arm'">arm-$(_LinuxAbi)eabi$(_LinuxFloatAbi)-$(_Objcopy)</_Objcopy>
<_Objcopy Condition="'$(Platform)' == 'armv6'">arm-$(_LinuxAbi)eabi$(_LinuxFloatAbi)-$(_Objcopy)</_Objcopy>
<_Objcopy Condition="'$(Platform)' == 'arm64'">aarch64-$(_LinuxAbi)-$(_Objcopy)</_Objcopy>
<_Objcopy Condition="'$(Platform)' == 'riscv64'">riscv64-$(_LinuxAbi)-$(_Objcopy)</_Objcopy>
<_Objcopy Condition="'$(Platform)' == 's390x'">s390x-$(_LinuxAbi)-$(_Objcopy)</_Objcopy>
<_Objcopy Condition="'$(Platform)' == 'ppc64le'">powerpc64le-$(_LinuxAbi)-$(_Objcopy)</_Objcopy>
<_Objcopy Condition="'$(Platform)' == 'x64'">x86_64-$(_LinuxAbi)-$(_Objcopy)</_Objcopy>
<_Objcopy Condition="'$(Platform)' == 'x86'">i686-$(_LinuxAbi)-$(_Objcopy)</_Objcopy>
<_Objcopy Condition="'$(TargetsAndroid)' == 'true' or '$(TargetsLinuxBionic)' == 'true'">$(ANDROID_NDK_ROOT)/toolchains/llvm/prebuilt/$(MonoToolchainPrebuiltOS)/bin/llvm-objcopy</_Objcopy>
<_Objcopy Condition="'$(TargetsLinuxMusl)' == 'true' and '$(CrossBuild)' != 'true'">objcopy</_Objcopy>

<_ObjcopyPrefix Condition="'$(MonoCrossDir)' != '' and '$(Platform)' == 'riscv64'">llvm-objcopy-</_ObjcopyPrefix>
</PropertyGroup>
Expand Down
8 changes: 8 additions & 0 deletions src/mono/mono/mini/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,10 @@ if(NOT DISABLE_SHARED_LIBS)
target_compile_definitions(monosgen-objects PRIVATE -DMONO_DLL_EXPORT)
endif()
target_sources(monosgen-shared PRIVATE $<TARGET_OBJECTS:eglib_objects>)
# Alpine Linux implements ucontext in a different library
if(CLR_CMAKE_HOST_ALPINE_LINUX AND TARGET_S390X)
target_link_libraries(monosgen-shared PRIVATE ucontext)
endif(CLR_CMAKE_HOST_ALPINE_LINUX AND TARGET_S390X)
set_target_properties(monosgen-shared PROPERTIES OUTPUT_NAME ${MONO_SHARED_LIB_NAME})
target_link_libraries (monosgen-shared PRIVATE monoapi)
target_include_directories (monosgen-shared PRIVATE monoapi)
Expand Down Expand Up @@ -534,6 +538,10 @@ if(NOT DISABLE_EXECUTABLES)
set_target_properties(mono-sgen PROPERTIES OUTPUT_NAME mono-aot-cross)
endif()
target_link_libraries(mono-sgen PRIVATE monoapi monosgen-static ${OS_LIBS} ${LLVM_LIBS} ${ICU_LIBS} ${Z_LIBS})
# Alpine Linux implements ucontext in a different library
if(CLR_CMAKE_HOST_ALPINE_LINUX AND TARGET_S390X)
target_link_libraries(mono-sgen PRIVATE ucontext)
endif(CLR_CMAKE_HOST_ALPINE_LINUX AND TARGET_S390X)
if(NOT DISABLE_COMPONENTS AND STATIC_COMPONENTS AND NOT DISABLE_LINK_STATIC_COMPONENTS)
# if components are built statically, link them into runtime.
target_sources(mono-sgen PRIVATE "${mono-components-objects}")
Expand Down
29 changes: 29 additions & 0 deletions src/mono/mono/mini/mini-runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -4393,6 +4393,30 @@ mini_llvm_init (void)
#endif
}

#ifdef ENSURE_PRIMARY_STACK_SIZE
/*++
Function:
EnsureStackSize

Abstract:
This fixes a problem on MUSL where the initial stack size reported by the
pthread_attr_getstack is about 128kB, but this limit is not fixed and
akoeplinger marked this conversation as resolved.
Show resolved Hide resolved
the stack can grow dynamically. The problem is that it makes the
functions ReflectionInvocation::[Try]EnsureSufficientExecutionStack
to fail for real life scenarios like e.g. compilation of corefx.
Since there is no real fixed limit for the stack, the code below
ensures moving the stack limit to a value that makes reasonable
real life scenarios work.

--*/
static MONO_NO_OPTIMIZATION MONO_NEVER_INLINE void
ensure_stack_size (size_t size)
{
volatile uint8_t *s = (uint8_t *)g_alloca(size);
*s = 0;
}
#endif // ENSURE_PRIMARY_STACK_SIZE

void
mini_add_profiler_argument (const char *desc)
{
Expand Down Expand Up @@ -4554,6 +4578,11 @@ mini_init (const char *filename)
mono_w32handle_init ();
#endif

#ifdef ENSURE_PRIMARY_STACK_SIZE
// TODO: https://github.com/dotnet/runtime/issues/72920
ensure_stack_size (5 * 1024 * 1024);
#endif // ENSURE_PRIMARY_STACK_SIZE

mono_thread_info_runtime_init (&ticallbacks);

if (g_hasenv ("MONO_DEBUG")) {
Expand Down
8 changes: 8 additions & 0 deletions src/mono/mono/utils/mono-context.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@
#ifndef __MONO_MONO_CONTEXT_H__
#define __MONO_MONO_CONTEXT_H__

/*
* Handle non-gnu libc versions with nothing in features.h
* We have no idea what they're compatible with, so always fail.
*/
#ifndef __GLIBC_PREREQ
ayakael marked this conversation as resolved.
Show resolved Hide resolved
# define __GLIBC_PREREQ(x,y) 0
#endif
ayakael marked this conversation as resolved.
Show resolved Hide resolved

#include "mono-compiler.h"
#include "mono-sigcontext.h"
#include "mono-machine.h"
Expand Down
2 changes: 1 addition & 1 deletion src/native/libs/System.Native/pal_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -1325,7 +1325,7 @@ int32_t SystemNative_CopyFile(intptr_t sourceFd, intptr_t destinationFd, int64_t
// Try copying data using a copy-on-write clone. This shares storage between the files.
if (sourceLength != 0)
{
while ((ret = ioctl(outFd, FICLONE, inFd)) < 0 && errno == EINTR);
while ((ret = ioctl(outFd, (int)FICLONE, inFd)) < 0 && errno == EINTR);
Copy link
Contributor

@Sapana-Khemkar Sapana-Khemkar Feb 9, 2023

Choose a reason for hiding this comment

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

@ayakael
Why FICLONE casted to int? Second argument of ioctl function is unsigned long and not int.
This caused ppc64le build error. Can you please revert this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was a proposal by @am11, as build failed without setting Wno-sign-conversion. See: #76500 (comment).

Here is the exact error:

/var/build/dotnet7/testing/dotnet7-stage0/src/dotnet-e6dd91c290b808f971a1ac69c2fb29395bbf1051/src/runtime/src/native/libs/System.Native/pal_io.c(1270,36): error GBBD8A67C: implicit conversion changes signedness: 'unsigned long' to 'int' [-Wsign-conversion] [/var/build/dotnet7/testing/dotnet7-stage0/src/dotnet-e6dd91c290b808f971a1ac69c2fb29395bbf1051/src/runtime/src/native/libs/build-native.proj]
/var/build/dotnet7/testing/dotnet7-stage0/src/dotnet-e6dd91c290b808f971a1ac69c2fb29395bbf1051/src/runtime/src/native/libs/System.Native/pal_io.c(1270,36): error GBBD8A67C: implicit conversion changes signedness: 'unsigned long' to 'int' [-Wsign-conversion] [/var/build/dotnet7/testing/dotnet7-stage0/src/dotnet-e6dd91c290b808f971a1ac69c2fb29395bbf1051/src/runtime/src/native/libs/build-native.proj]

Copy link
Member

Choose a reason for hiding this comment

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

If you change last argument to unsigned long here:

#define FICLONE _IOW(0x94, 9, int)
without this (int)FICLONE cast, does it fix both linux-ppc64le and linux-musl-ppc64le builds?

Copy link
Contributor

Choose a reason for hiding this comment

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

That would be incorrect - the _IOW macro uses sizeof its last argument as a component of the resulting value, so changing that size from 4 to 8 will result in an incorrect value of FICLONE.

This issue seems to be caused by some difference in the definition of ioctl with musl. In glibc, we have this:

extern int ioctl (int __fd, unsigned long int __request, ...);

Is this declared differently in musl?

Copy link
Member

Choose a reason for hiding this comment

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

It is a ppc64le specific issue, where musl and glibc have different definitions. Rest of the platform matrix (including linux-musl-x64/arm64/arm etc..) is fine either way.

That would be incorrect - the _IOW macro uses sizeof

I see, so the current definition is also incorrect; should be size_t instead of int, right?

Copy link
Contributor

Choose a reason for hiding this comment

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

It is a ppc64le specific issue, where musl and glibc have different definitions. Rest of the platform matrix (including linux-musl-x64/arm64/arm etc..) is fine either way.

The only difference between ppc64le and other platforms is the numerical value of FICLONE here. On ppc64le, this value is 0x80049409 while on other platforms it is 0x40049409 due to a different definition of the _IOW macro. This means on other platforms the value is the same interpreted as int and unsigned long, so there's no compiler warning. On ppc64le, the value is negative interpreted as int and positive interpreted as unsigned long, that's why we get the warning. The type mismatch itself is currently present on all platforms.

That would be incorrect - the _IOW macro uses sizeof

I see, so the current definition is also incorrect; should be size_t instead of int, right?

No. We must use sizeof (int), i.e. 4, here; this flows into the construction of that value above.

Copy link
Contributor

Choose a reason for hiding this comment

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

This issue seems to be caused by some difference in the definition of ioctl with musl. In glibc, we have this:

extern int ioctl (int __fd, unsigned long int __request, ...);

Is this declared differently in musl?

Indeed this seems to be problem. In the musl headers I see instead:

int ioctl (int, int, ...);

I guess as long as the two headers differ, there's probably no way to fix the source to work with both libcs, except for using some #ifdef ...

copied = ret == 0;
}
#endif
Expand Down