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

Native AOT classlib compiles with [DLLImport° as cheating dynamic library #83353

Closed
DeafMan1983 opened this issue Mar 13, 2023 · 4 comments
Closed
Labels
area-Interop-coreclr question Answer questions and provide assistance, not an issue with source code or documentation.

Comments

@DeafMan1983
Copy link

DeafMan1983 commented Mar 13, 2023

Hello,

Woah I expected that I have written with [DLLImport()] in classlib project

But why does ldd not show if I added DLLImport as l(library-name) I will explain next line...

Example I write Class1.cs

using System.Runtime.InteropServices;
using System.Text;

namespace HelloFromString;
public class Class1
{
    [UnmanagedCallersOnly(EntryPoint = "write_line")]
    public static int WriteLine(IntPtr pString)
    {
        try
        {
            string str = Marshal.PtrToStringAnsi(pString);

            Console.WriteLine(str);
        }
        catch
        {
            return -1;
        }
        return 0;
    }

    [UnmanagedCallersOnly(EntryPoint = "hello_world")]
    private static void Hello()
    {
        Console.WriteLine("Hello World!");
    }

    [DllImport("libX11")]
    private static extern nint XOpenDisplay(nint display_name);

    [DllImport("libX11")]
    private static extern int XCloseDisplay(nint display);

    [DllImport("libX11")]
    private static extern int XDefaultScreen(nint display);

    [DllImport("libX11")]
    private static extern short XDisplayWidth(nint display, int scr_nbr);

    [DllImport("libX11")]
    private static extern short XDisplayHeight(nint display, int scr_nbr);

    [UnmanagedCallersOnly(EntryPoint = "custom_open")]
    public static nint Open(nint display_name)
    {
        return XOpenDisplay(display_name);
    }

    [UnmanagedCallersOnly(EntryPoint = "custom_close")]
    public static int Close(nint display)
    {
        return XCloseDisplay(display);
    }

    [UnmanagedCallersOnly(EntryPoint = "custom_default_screen")]
    public static int DefScr(nint display)
    {
        return XDefaultScreen(display);
    }

    [UnmanagedCallersOnly(EntryPoint = "custom_d_width")]
    public static short DWwidth(nint display, int scr_nbr)
    {
        return XDisplayWidth(display, scr_nbr);
    }

    [UnmanagedCallersOnly(EntryPoint = "custom_d_height")]
    public static short DHeight(nint display, int scr_nbr)
    {
        return XDisplayHeight(display, scr_nbr);
    }
}

I check native compiled example.so with ldd
what does it happen but native aot won't show when I already use [DLLImport("libX11")] as -lX11 in clang / gcc line
Result:

ldd bin/Release/net7.0/linux-x64/native/example.so
	linux-vdso.so.1 (0x00007ffea7ee6000)
	libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fcec1c00000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fcec1f19000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fcec1800000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fcec241a000)
	libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fcec23e2000)

That is unfair because dotnet cheats to load hidden dynamical libraries...
Where is libX11.so.6 from ldd when you have used DLLImport?

Please fix if native aot compilation will convert DLLImport into static library *.a then generates to clang's line -l(library-name) and include-path also.

And I try console project:
Result:

namespace Test;

using System.Runtime.InteropServices;

class Program
{
    [DllImport("example")]
    private static extern int write_line(IntPtr pString);
    
    [DllImport("example")]
    private static extern void hello_world();
    private static int WriteLine(string message)
    {
        return write_line(Marshal.StringToHGlobalAnsi(message));
    }

    [DllImport("example")]
    private static extern nint custom_open(nint display_name);

    [DllImport("example")]
    private static extern int custom_close(nint display);

    [DllImport("example")]
    private static extern int custom_default_screen(nint display);

    [DllImport("example")]
    private static extern short custom_d_width(nint display, int screen_number);

    [DllImport("example")]
    private static extern short custom_d_height(nint display, int screen_number);

    static void Main(string[] args)
    {
        int meshdl = WriteLine("From shared object");
        Console.WriteLine("Hello,", Convert.ToString(meshdl));
        hello_world();

        nint display = custom_open(nint.Zero);
        if (display == nint.Zero)
        {
            Console.WriteLine("Error");
            Environment.Exit(1);
        }

        Console.WriteLine("Initializing custom X11!");

        int screen_number = custom_default_screen(display);
        Console.WriteLine("Screen Number is {0}", screen_number);

        int d_width = custom_d_width(display, screen_number);
        int d_height = custom_d_height(display, screen_number);
        Console.WriteLine("\tScreen Width is {0}", d_width);
        Console.WriteLine("\tScreen Height is {0}", d_height);

        custom_close(display);
    }
}

And Terminal in VS Code output:

From shared object
Hello,
Hello World!
Initializing custom X11!
Screen Number is 0
        Screen Width is 1920
        Screen Height is 1080

Woah it works fine. But ldd doesn't know where is DLLImport's path-to-library? That is why I need to give suggestion generation of dotnet into static library name like -lX11 from libX11.a

Example:
If you want SDL3 or GLFW3 into native aot via DLLImport and native aot compilation will change DLLImport to library-name like clang ..... -lSDL3 ...
then somebody checks with ldd then it will list in native aot compiled *.so file from classlib project.

ldd bin/Release/net7.0/linux-x64/native/example.so
	linux-vdso.so.1 (0x00007ffea7ee6000)
	libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fcec1c00000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fcec1f19000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fcec1800000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fcec241a000)
	libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fcec23e2000)
	libSDL3.so => /lib/x86_64-linux-gnu/libSDL3.so

That is proof from generated path to lib-name. I hope you know about cheating is not allowed. And I want everyone is seriously. I know Dotnet is not cheater. That is why I find bug when native aot doesn't know while you write with DLLImport in classlib-project then native aot compiler should catch path if it find *.a "Hu that is valid or current a file of same shared libraries like Windows 10 has dll file and lib file.

I would like to imagine about our development if native aot process find path User32.dll then it knows -> where is User32.lib and it catches lib file from Windows SDK Installation's Path.

Do you think Dotnet native aot compilation can catch static library if somebody uses DLLImport and builds end-user application. And shows proof of ldd.

Is it realistically?

Thanks!

@ghost ghost added the untriaged New issue has not been triaged by the area owner label Mar 13, 2023
@jkotas jkotas added the question Answer questions and provide assistance, not an issue with source code or documentation. label Mar 13, 2023
@jkotas
Copy link
Member

jkotas commented Mar 13, 2023

.NET runtime loads shared libraries dynamically by default. Native AOT has the same default behavior.

If you would like the unmanaged shared library to be linked directly such that it shows up in ldd output, add it to DirectPInvoke item group in your project file, e.g. <DirectPInvoke Include="libX11" />.

This is documented in https://github.com/dotnet/runtime/blob/main/src/coreclr/nativeaot/docs/interop.md#direct-pinvoke-calls

@jkotas jkotas closed this as completed Mar 13, 2023
@ghost ghost removed the untriaged New issue has not been triaged by the area owner label Mar 13, 2023
@DeafMan1983
Copy link
Author

@jkotas thanks! That's great for direct invoke. That is weird bug. That's not question. It looks like cheating from hidden loading shared libraries who doesn't know to open important shared libraries like Ubuntu 22.04 forgot to install required deps when end user opens native aot compiled dotapps or checks ldd. They think dotnet cheats from forgotten invoking libraries. That's why dotnet made criminally. That's why we need support for improving generation of automatical directinvoke output after native aot compilation and end user will know when dotapp opens by user and throws errors and it says to need to install shared libraries. Thanks!

@jkotas
Copy link
Member

jkotas commented Mar 14, 2023

Our principle is to avoid unnecessary behavior differences between standard .NET runtime and native AOT compiled binaries. .NET runtime loads shared libraries on demand and number of popular NuGet packages depend on this behavior. We are not going to change the default behavior. It would be a hard-to-justify breaking change.

@DeafMan1983
Copy link
Author

DeafMan1983 commented Mar 14, 2023

Okay that's our decision of dotnet. Thanks for understanding! I will use manual with directinvoke.

// EDIT:
I have checked but dotnet native aot can't find to library path if it finds libX11.a means -lX11

I already tried.

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <PublishAot>true</PublishAot>
  </PropertyGroup>

  <ItemGroup>
    <DirectPInvoke Include="libX11" />
    <NativeLibrary Include="libX11.a" Condition="!$(RuntimeIdentifier.StartsWith('win'))" />
  </ItemGroup>

</Project>

But it throws error because it doesn't find libX11.a from /usr/lib/x86_64-linux-gnu

dotnet publish -r linux-x64 -c Release
then output shows error cause it doesn't know like correct for -lX11 of libX11.a

Result:

MSBuild version 17.4.1+9a89d02ff for .NET
  Determining projects to restore...
  All projects are up-to-date for restore.
/home/jens/Documents/nativeaot/HelloFromString/Class1.cs(12,26): warning CS8600: Converting null literal or possible null value to non-nullable type. [/home/jens/Documents/nativeaot/HelloFromString/HelloFromString.csproj]
  HelloFromString -> /home/jens/Documents/nativeaot/HelloFromString/bin/Release/net7.0/linux-x64/HelloFromString.dll
  Generating native code
clang : error : no such file or directory: 'libX11.a' [/home/jens/Documents/nativeaot/HelloFromString/HelloFromString.csproj]
/home/jens/.dotnet/sdk/7.0.102/Sdks/Microsoft.DotNet.ILCompiler/build/Microsoft.NETCore.Native.targets(346,5): error MSB3073: The command ""clang" "obj/Release/net7.0/linux-x64/native/HelloFromString.o" -o "bin/Release/net7.0/linux-x64/native/HelloFromString.so" -Wl,--version-script=obj/Release/net7.0/linux-x64/native/HelloFromString.exports libX11.a /home/jens/.nuget/packages/runtime.linux-x64.microsoft.dotnet.ilcompiler/7.0.2/sdk/libbootstrapperdll.a /home/jens/.nuget/packages/runtime.linux-x64.microsoft.dotnet.ilcompiler/7.0.2/sdk/libRuntime.WorkstationGC.a /home/jens/.nuget/packages/runtime.linux-x64.microsoft.dotnet.ilcompiler/7.0.2/framework/libSystem.Native.a /home/jens/.nuget/packages/runtime.linux-x64.microsoft.dotnet.ilcompiler/7.0.2/framework/libSystem.Globalization.Native.a /home/jens/.nuget/packages/runtime.linux-x64.microsoft.dotnet.ilcompiler/7.0.2/framework/libSystem.IO.Compression.Native.a /home/jens/.nuget/packages/runtime.linux-x64.microsoft.dotnet.ilcompiler/7.0.2/framework/libSystem.Net.Security.Native.a /home/jens/.nuget/packages/runtime.linux-x64.microsoft.dotnet.ilcompiler/7.0.2/framework/libSystem.Security.Cryptography.Native.OpenSsl.a -g -Wl,-rpath,'$ORIGIN' -Wl,--build-id=sha1 -Wl,--as-needed -Wl,-e0x0 -pthread -lstdc++ -ldl -lm -lz -lrt -shared -Wl,-z,relro -Wl,-z,now -Wl,--require-defined,NativeAOT_StaticInitialization -Wl,--discard-all -Wl,--gc-sections" exited with code 1. [/home/jens/Documents/nativeaot/HelloFromString/HelloFromString.csproj]

That is catastrophically.
How do I add -lX11 in native aot compilation

If native aot compilation throws error then I add manually copy and past remove libX11.a and find -lrt and add -lX11 and compilation by hand and I prompt then it shows not errors.

clang "obj/Release/net7.0/linux-x64/native/HelloFromString.o" -o "bin/Release/net7.0/linux-x64/native/HelloFromString.so" -Wl,--version-script=obj/Release/net7.0/linux-x64/native/HelloFromString.exports
/home/jens/.nuget/packages/runtime.linux-x64.microsoft.dotnet.ilcompiler/7.0.2/sdk/libbootstrapperdll.a /home/jens/.nuget/packages/runtime.linux-x64.microsoft.dotnet.ilcompiler/7.0.2/sdk/libRuntime.WorkstationGC.a /home/jens/.nuget/packages/runtime.linux-x64.microsoft.dotnet.ilcompiler/7.0.2/framework/libSystem.Native.a /home/jens/.nuget/packages/runtime.linux-x64.microsoft.dotnet.ilcompiler/7.0.2/framework/libSystem.Globalization.Native.a /home/jens/.nuget/packages/runtime.linux-x64.microsoft.dotnet.ilcompiler/7.0.2/framework/libSystem.IO.Compression.Native.a /home/jens/.nuget/packages/runtime.linux-x64.microsoft.dotnet.ilcompiler/7.0.2/framework/libSystem.Net.Security.Native.a /home/jens/.nuget/packages/runtime.linux-x64.microsoft.dotnet.ilcompiler/7.0.2/framework/libSystem.Security.Cryptography.Native.OpenSsl.a -g -Wl,-rpath,'$ORIGIN' -Wl,--build-id=sha1 -Wl,--as-needed -Wl,-e0x0 -pthread -lstdc++ -lX11 -ldl -lm -lz -lrt -shared -Wl,-z,relro -Wl,-z,now -Wl,--require-defined,NativeAOT_StaticInitialization -Wl,--discard-all -Wl,--gc-sections

Check ldd it is really valid and correct.

ldd bin/Release/net7.0/linux-x64/native/HelloFromString.so
        linux-vdso.so.1 (0x00007ffc1233f000)
        libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f26b7400000)
        libX11.so.6 => /lib/x86_64-linux-gnu/libX11.so.6 (0x00007f26b76c0000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f26b7319000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f26b7000000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f26b7c63000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f26b7c2b000)
        libxcb.so.1 => /lib/x86_64-linux-gnu/libxcb.so.1 (0x00007f26b7bff000)
        libXau.so.6 => /lib/x86_64-linux-gnu/libXau.so.6 (0x00007f26b7bf9000)
        libXdmcp.so.6 => /lib/x86_64-linux-gnu/libXdmcp.so.6 (0x00007f26b7bf1000)
        libbsd.so.0 => /lib/x86_64-linux-gnu/libbsd.so.0 (0x00007f26b7bd9000)
        libmd.so.0 => /lib/x86_64-linux-gnu/libmd.so.0 (0x00007f26b76b3000)

That is correctly. but how do I add -l(library-name) in csproj file? Lol! You fool me :/ Because I should use in csproj.

That is really impossible. But why does dotnet show NativeLibrary's Include with -lc but in directinvoke document is wrong

<ItemGroup>
  <!-- Generate direct PInvoke calls for everything in __Internal -->
  <!-- This option replicates Mono AOT behavior that generates direct PInvoke calls for __Internal -->
  <DirectPInvoke Include="__Internal" />
  <!-- Generate direct PInvoke calls for everything in libc (also matches libc.so on Linux or libc.dylib on macOS) --> 
  <DirectPInvoke Include="libc" />
  <!-- Generate direct PInvoke calls for Sleep in kernel32 (also matches kernel32.dll on Windows) -->
  <DirectPInvoke Include="kernel32!Sleep" />
  <!-- Generate direct PInvoke for all APIs listed in DirectXAPIs.txt -->
  <DirectPInvokeList Include="DirectXAPIs.txt" />
</ItemGroup>

It should like for Linux only:

<ItemGroup>
  <!-- Generate direct PInvoke calls for everything in __Internal -->
  <!-- This option replicates Mono AOT behavior that generates direct PInvoke calls for __Internal -->
  <DirectPInvoke Include="__Internal" />
  <!-- Generate direct PInvoke calls for everything in libc (also matches libc.so on Linux or libc.dylib on macOS) -->
<!-- Important for linking *.a from shared library like communication of static library --> 
  <DirectPInvoke Include="-lc" />
  <!-- Generate direct PInvoke calls for Sleep in kernel32 (also matches kernel32.dll on Windows) -->
  <DirectPInvoke Include="kernel32!Sleep" />
  <!-- Generate direct PInvoke for all APIs listed in DirectXAPIs.txt -->
  <DirectPInvokeList Include="DirectXAPIs.txt" />
</ItemGroup>

That is correct for Unix if you use Linux then you need use "-l(library-name)" in NativeLibrary's Include

If you use pkgconfig like Gtk3 or Gtk4 then you should use
<NativeLibrary Include="'pkg-config gtk+-3.0 --libs'" />

Do you think that is correct or wrongly?

@dotnet dotnet locked as resolved and limited conversation to collaborators Apr 13, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-Interop-coreclr question Answer questions and provide assistance, not an issue with source code or documentation.
Projects
None yet
Development

No branches or pull requests

2 participants