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

Running .NET 7 single file binary on Apple M1 chip causes app to be "killed" #79267

Closed
vitek-karas opened this issue Dec 6, 2022 Discussed in #79229 · 36 comments
Closed

Running .NET 7 single file binary on Apple M1 chip causes app to be "killed" #79267

vitek-karas opened this issue Dec 6, 2022 Discussed in #79229 · 36 comments
Assignees
Milestone

Comments

@vitek-karas
Copy link
Member

Discussed in #79229

Originally posted by rcdailey December 5, 2022
I have a Github Workflow that compiles my .NET 7 console application using a windows agent. It runs this in a powershell script:

dotnet publish MyProject.csproj `
    --output publish `
    --configuration Release`
    --runtime osx-arm64 `
    --self-contained true `
    -p:PublishSingleFile=true `
    -p:IncludeNativeLibrariesForSelfExtract=true `
    -p:PublishReadyToRunComposite=true `
    -p:PublishReadyToRunShowWarnings=true `
    -p:EnableCompressionInSingleFile=true 

This is the platform I'm running on:

enter image description here

My application is not signed, but Mac lets me bypass that for testing purposes. Eventually it just fails like so:

[1]    37470 killed     ./myconsoleapp --help

I get no further diagnostics. What steps can I take to further diagnose the issue? Why is osx-arm64 not working but osx-x64 works just fine?

@ghost ghost added the untriaged New issue has not been triaged by the area owner label Dec 6, 2022
@ghost
Copy link

ghost commented Dec 6, 2022

Tagging subscribers to this area: @agocke, @vitek-karas, @VSadov
See info in area-owners.md if you want to be subscribed.

Issue Details

Discussed in #79229

Originally posted by rcdailey December 5, 2022
I have a Github Workflow that compiles my .NET 7 console application using a windows agent. It runs this in a powershell script:

dotnet publish MyProject.csproj `
    --output publish `
    --configuration Release`
    --runtime osx-arm64 `
    --self-contained true `
    -p:PublishSingleFile=true `
    -p:IncludeNativeLibrariesForSelfExtract=true `
    -p:PublishReadyToRunComposite=true `
    -p:PublishReadyToRunShowWarnings=true `
    -p:EnableCompressionInSingleFile=true 

This is the platform I'm running on:

enter image description here

My application is not signed, but Mac lets me bypass that for testing purposes. Eventually it just fails like so:

[1]    37470 killed     ./myconsoleapp --help

I get no further diagnostics. What steps can I take to further diagnose the issue? Why is osx-arm64 not working but osx-x64 works just fine?

Author: vitek-karas
Assignees: -
Labels:

area-Single-File

Milestone: -

@vitek-karas
Copy link
Member Author

Comment by @am11 :

this seems related to #49855.
I get bus error (138) on osx-arm64 even with hello-world app, e.g. using daily builds:

$ dotnet8 publish -o publish -p:PublishSingleFile=true -p:EnableCompressionInSingleFile=true
$ ./publish/helloworldapp # bus error

$ dotnet8 publish -o publish -p:PublishSingleFile=true -p:EnableCompressionInSingleFile=false
$ ./publish/helloworldapp # works

@vitek-karas
Copy link
Member Author

/cc @elinor-fung

@am11
Copy link
Member

am11 commented Dec 6, 2022

It is failing in PEImageLayout::ApplyBaseRelocations:

(lldb) r
Process 95834 launched: '/Users/am11/projects/helloworldapp/publish/helloworldapp' (arm64)
Process 95834 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x1053bdbd4)
    frame #0: 0x0000000100576838 helloworldapp`PEImageLayout::ApplyBaseRelocations(bool) + 488
helloworldapp`PEImageLayout::ApplyBaseRelocations:
->  0x100576838 <+488>: str    x10, [x9], #0x8
    0x10057683c <+492>: cmp    x20, x9
    0x100576840 <+496>: csel   x20, x20, x9, hi
    0x100576844 <+500>: b      0x10057680c               ; <+444>
Target 0: (helloworldapp) stopped.

(lldb) clrstack -f
OS Thread Id: 0x127edfa (1)
        Child SP               IP Call Site
000000016FDFE080 0000000100576838 helloworldapp!PEImageLayout::ApplyBaseRelocations(bool) + 488
000000016FDFE110 0000000100576D74 helloworldapp!ConvertedImageLayout::ConvertedImageLayout(FlatImageLayout*) + 364
000000016FDFE130 0000000100576380 helloworldapp!PEImageLayout::LoadConverted(PEImage*) + 188
000000016FDFE160 00000001000F7BF0 helloworldapp!PEImage::GetOrCreateLayoutInternal(unsigned int) + 68
000000016FDFE190 00000001000F6BCC helloworldapp!PEImage::GetOrCreateLayout(unsigned int) + 160
000000016FDFE1D0 00000001000F4854 helloworldapp!PEAssembly::EnsureLoaded() + 56
000000016FDFE430 000000010005B88C helloworldapp!Assembly::Init(AllocMemTracker*, LoaderAllocator*) + 112
000000016FDFE470 000000010005C650 helloworldapp!Assembly::Create(BaseDomain*, PEAssembly*, DebuggerAssemblyControlFlags, int, AllocMemTracker*, LoaderAllocator*) + 412
000000016FDFE510 000000010008E9C4 helloworldapp!DomainAssembly::Allocate() + 76
000000016FDFE740 000000010008E850 helloworldapp!DomainAssembly::DoIncrementalLoad(FileLoadLevel) + 144
000000016FDFE770 000000010005344C helloworldapp!AppDomain::TryIncrementalLoad(DomainAssembly*, FileLoadLevel, Wrapper<FileLoadLock*, &(void DoNothing<FileLoadLock*>(FileLoadLock*)), &(FileLoadLock::HolderLeave(FileLoadLock*)), 0ul, &(int CompareDefault<FileLoadLock*>(FileLoadLock*, FileLoadLock*)), true>&) + 212
000000016FDFE820 0000000100052908 helloworldapp!AppDomain::LoadDomainAssembly(FileLoadLock*, FileLoadLevel) + 256
000000016FDFE8B0 0000000100052CC4 helloworldapp!AppDomain::LoadDomainAssemblyInternal(AssemblySpec*, PEAssembly*, FileLoadLevel) + 524
000000016FDFE970 000000010004F3AC helloworldapp!SystemDomain::LoadBaseSystemClasses() + 52
000000016FDFE9A0 000000010004F0B0 helloworldapp!SystemDomain::Init() + 580
000000016FDFEA10 000000010057487C helloworldapp!EEStartupHelper() + 1732
000000016FDFEAD0 0000000100573F40 helloworldapp!EEStartup() + 220
000000016FDFEB30 0000000100573E4C helloworldapp!EnsureEEStarted() + 340
000000016FDFEB60 0000000100086728 helloworldapp!CorHost2::Start() + 116
000000016FDFEB90 00000001005783B4 helloworldapp!coreclr_initialize + 724
000000016FDFEC50 000000010001F82C helloworldapp!coreclr_t::create(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, char const*, char const*, coreclr_property_bag_t const&, std::__1::unique_ptr<coreclr_t, std::__1::default_delete<coreclr_t> >&) + 420
000000016FDFED60 000000010002C654 helloworldapp!(anonymous namespace)::create_coreclr() + 432
000000016FDFEDD0 000000010002C128 helloworldapp!corehost_main + 160
000000016FDFEF20 000000010000C8F4 helloworldapp!fx_muxer_t::handle_exec_host_command(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, host_startup_info_t const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::unordered_map<known_options, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, known_options_hash, std::__1::equal_to<known_options>, std::__1::allocator<std::__1::pair<known_options const, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > > > > const&, int, char const**, int, host_mode_t, bool, char*, int, int*) + 1328
000000016FDFF080 000000010000B9E8 helloworldapp!fx_muxer_t::execute(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, int, char const**, host_startup_info_t const&, char*, int, int*) + 860
000000016FDFF190 000000010000848C helloworldapp!hostfxr_main_bundle_startupinfo + 196
000000016FDFF240 000000010004C93C helloworldapp!exe_start(int, char const**) + 1132
000000016FDFF370 000000010004CC20 helloworldapp!main + 152
000000016FDFF3D0 0000000196033E50 dyld!start + 2544

(lldb) disas
helloworldapp`PEImageLayout::ApplyBaseRelocations:
    0x100576650 <+0>:   sub    sp, sp, #0x90
    0x100576654 <+4>:   stp    x28, x27, [sp, #0x30]
    0x100576658 <+8>:   stp    x26, x25, [sp, #0x40]
    0x10057665c <+12>:  stp    x24, x23, [sp, #0x50]
    0x100576660 <+16>:  stp    x22, x21, [sp, #0x60]
    0x100576664 <+20>:  stp    x20, x19, [sp, #0x70]
    0x100576668 <+24>:  stp    x29, x30, [sp, #0x80]
    0x10057666c <+28>:  add    x29, sp, #0x80
    0x100576670 <+32>:  mov    x19, x0
    0x100576674 <+36>:  ldr    x9, [x19, #0x8]!
    0x100576678 <+40>:  ldr    w8, [x19, #0xc]
    0x10057667c <+44>:  orr    w8, w8, #0x4
    0x100576680 <+48>:  str    w8, [x19, #0xc]
    0x100576684 <+52>:  ldrsw  x8, [x9, #0x3c]
    0x100576688 <+56>:  add    x8, x9, x8
    0x10057668c <+60>:  ldrh   w10, [x8, #0x18]
    0x100576690 <+64>:  cmp    w10, #0x10b
    0x100576694 <+68>:  b.ne   0x1005766a8               ; <+88>
    0x100576698 <+72>:  ldr    w11, [x8, #0x34]
    0x10057669c <+76>:  subs   x25, x9, x11
    0x1005766a0 <+80>:  b.ne   0x1005766b4               ; <+100>
    0x1005766a4 <+84>:  b      0x1005768f4               ; <+676>
    0x1005766a8 <+88>:  ldr    x11, [x8, #0x30]
    0x1005766ac <+92>:  subs   x25, x9, x11
    0x1005766b0 <+96>:  b.eq   0x1005768f4               ; <+676>
    0x1005766b4 <+100>: cmp    w10, #0x10b
    0x1005766b8 <+104>: mov    w9, #0x88
    0x1005766bc <+108>: mov    w10, #0x78
    0x1005766c0 <+112>: csel   x9, x10, x9, eq
    0x1005766c4 <+116>: add    x8, x8, x9
    0x1005766c8 <+120>: ldp    w1, w28, [x8, #0x28]
    0x1005766cc <+124>: mov    x0, x19
    0x1005766d0 <+128>: mov    w2, #0x0
    0x1005766d4 <+132>: bl     0x100310e94               ; PEDecoder::GetRvaData(unsigned int, IsNullOK) const
    0x1005766d8 <+136>: cbz    w28, 0x100576914          ; <+708>
    0x1005766dc <+140>: mov    x12, #0x0
    0x1005766e0 <+144>: mov    x13, #0x0
    0x1005766e4 <+148>: mov    w24, #0x0
    0x1005766e8 <+152>: mov    x11, #0x0
    0x1005766ec <+156>: stp    x0, xzr, [sp, #0x20]
    0x1005766f0 <+160>: mov    w27, #0x0
    0x1005766f4 <+164>: mov    x23, #0x1fffffff8
    0x1005766f8 <+168>: str    w28, [sp, #0x1c]
    0x1005766fc <+172>: b      0x100576718               ; <+200>
    0x100576700 <+176>: mov    x11, x21
    0x100576704 <+180>: sub    x8, x20, x11
    0x100576708 <+184>: str    x8, [sp, #0x28]
    0x10057670c <+188>: add    w27, w22, w27
    0x100576710 <+192>: cmp    w27, w28
    0x100576714 <+196>: b.hs   0x1005768a8               ; <+600>
    0x100576718 <+200>: add    x8, x0, w27, uxtw
    0x10057671c <+204>: add    x26, x8, #0x8
    0x100576720 <+208>: ldp    w20, w22, [x8]
    0x100576724 <+212>: ldr    x8, [x19]
    0x100576728 <+216>: add    x21, x8, x20
    0x10057672c <+220>: sub    x8, x21, x12
    0x100576730 <+224>: cmp    x8, x13
    0x100576734 <+228>: b.lo   0x1005767f4               ; <+420>
    0x100576738 <+232>: mov    x28, x11
    0x10057673c <+236>: tst    w24, #0xf0
    0x100576740 <+240>: b.eq   0x10057674c               ; <+252>
    0x100576744 <+244>: mov    w0, #0x0
    0x100576748 <+248>: bl     0x1005fd110               ; PAL_JitWriteProtect
    0x10057674c <+252>: ldrh   w8, [x26]
    0x100576750 <+256>: and    w8, w8, #0xfff
    0x100576754 <+260>: add    w1, w20, w8
    0x100576758 <+264>: mov    x0, x19
    0x10057675c <+268>: bl     0x1003106ec               ; PEDecoder::RvaToSection(unsigned int) const
    0x100576760 <+272>: mov    x24, x0
    0x100576764 <+276>: ldr    w1, [x0, #0xc]
    0x100576768 <+280>: mov    x0, x19
    0x10057676c <+284>: mov    w2, #0x0
    0x100576770 <+288>: bl     0x100310e94               ; PEDecoder::GetRvaData(unsigned int, IsNullOK) const
    0x100576774 <+292>: mov    x12, x0
    0x100576778 <+296>: ldr    w13, [x24, #0x10]
    0x10057677c <+300>: ldr    w8, [x24, #0x24]
    0x100576780 <+304>: tbnz   w8, #0x1f, 0x100576798    ; <+328>
    0x100576784 <+308>: tbnz   w8, #0x1d, 0x1005767a8    ; <+344>
    0x100576788 <+312>: mov    w8, #0x0
    0x10057678c <+316>: ldr    x0, [sp, #0x20]
    0x100576790 <+320>: mov    x11, x28
    0x100576794 <+324>: b      0x1005767dc               ; <+396>
    0x100576798 <+328>: mov    w24, #0x0
    0x10057679c <+332>: ldr    x0, [sp, #0x20]
    0x1005767a0 <+336>: mov    x11, x28
    0x1005767a4 <+340>: b      0x1005767ec               ; <+412>
    0x1005767a8 <+344>: mov    w0, #0x1
    0x1005767ac <+348>: mov    x20, x12
    0x1005767b0 <+352>: mov    x23, x19
    0x1005767b4 <+356>: mov    x19, x13
    0x1005767b8 <+360>: bl     0x1005fd110               ; PAL_JitWriteProtect
    0x1005767bc <+364>: mov    x13, x19
    0x1005767c0 <+368>: mov    x19, x23
    0x1005767c4 <+372>: mov    x12, x20
    0x1005767c8 <+376>: ldr    w9, [x24, #0x24]
    0x1005767cc <+380>: and    w8, w9, #0x20000000
    0x1005767d0 <+384>: ldr    x0, [sp, #0x20]
    0x1005767d4 <+388>: mov    x11, x28
    0x1005767d8 <+392>: tbnz   w9, #0x1f, 0x100576898    ; <+584>
    0x1005767dc <+396>: cmp    w8, #0x0
    0x1005767e0 <+400>: mov    w8, #0x20
    0x1005767e4 <+404>: mov    w9, #0x2
    0x1005767e8 <+408>: csel   w24, w9, w8, eq
    0x1005767ec <+412>: ldr    w28, [sp, #0x1c]
    0x1005767f0 <+416>: mov    x23, #0x1fffffff8
    0x1005767f4 <+420>: add    x8, x22, x23
    0x1005767f8 <+424>: lsr    x9, x8, #1
    0x1005767fc <+428>: cbz    w9, 0x10057670c           ; <+188>
    0x100576800 <+432>: mov    x20, #0x0
    0x100576804 <+436>: ubfx   x8, x8, #1, #32
    0x100576808 <+440>: b      0x100576818               ; <+456>
    0x10057680c <+444>: add    x26, x26, #0x2
    0x100576810 <+448>: subs   x8, x8, #0x1
    0x100576814 <+452>: b.eq   0x100576848               ; <+504>
    0x100576818 <+456>: ldrh   w9, [x26]
    0x10057681c <+460>: and    w10, w9, #0xf000
    0x100576820 <+464>: cmp    w10, #0xa, lsl #12        ; =0xa000 
    0x100576824 <+468>: b.ne   0x10057680c               ; <+444>
    0x100576828 <+472>: and    x9, x9, #0xfff
    0x10057682c <+476>: add    x9, x21, x9
    0x100576830 <+480>: ldr    x10, [x9]
    0x100576834 <+484>: add    x10, x10, x25
->  0x100576838 <+488>: str    x10, [x9], #0x8
    0x10057683c <+492>: cmp    x20, x9
    0x100576840 <+496>: csel   x20, x20, x9, hi
    0x100576844 <+500>: b      0x10057680c               ; <+444>
    0x100576848 <+504>: tst    w24, #0xf0
    0x10057684c <+508>: b.eq   0x10057670c               ; <+188>
    0x100576850 <+512>: cbz    x20, 0x10057670c          ; <+188>
    0x100576854 <+516>: ldr    x8, [sp, #0x28]
    0x100576858 <+520>: add    x8, x11, x8
    0x10057685c <+524>: add    x8, x8, #0x1, lsl #12     ; =0x1000 
    0x100576860 <+528>: cmp    x21, x8
    0x100576864 <+532>: b.hs   0x100576870               ; <+544>
    0x100576868 <+536>: cmp    x21, x11
    0x10057686c <+540>: b.hs   0x100576704               ; <+180>
    0x100576870 <+544>: cbz    x11, 0x100576700          ; <+176>
    0x100576874 <+548>: mov    x26, x11
    0x100576878 <+552>: stp    x13, x12, [sp, #0x8]
    0x10057687c <+556>: bl     0x1006174dc               ; GetCurrentProcess
...

cc @janvorli

@am11
Copy link
Member

am11 commented Dec 6, 2022

$ dotnet8 publish -o publish -p:PublishSingleFile=true -p:EnableCompressionInSingleFile=true
$ ./publish/helloworldapp # bus error

Note: if we use x64 (pass -r osx-x64), the error disappears on the same M1 machine (x64 process runs under Rosetta2 emulation). So this is arm64 specific issue with compression enabled. Possibly happening since #67118.

@rcdailey
Copy link

rcdailey commented Dec 6, 2022

Thanks for the quick turnaround on this. Will this be fixed in .net 7 as a hot fix release? Until then, is there a workaround that I can use to get this working?

@rcdailey
Copy link

rcdailey commented Dec 6, 2022

I should also add, I observed this same behavior almost a year ago on .net 6 as well.

@am11
Copy link
Member

am11 commented Dec 6, 2022

is there a workaround that I can use to get this working?

If flipping EnableCompressionInSingleFile from true to false fixes the problem in your project, then that'd be the known workaround. 🙂

rcdailey added a commit to recyclarr/recyclarr that referenced this issue Dec 6, 2022
Having `EnableCompressionInSingleFile` set to `true` causes a crash when
launching Recyclarr on Apple M1 (arm64) chips. As a workaround, disable
this flag.

Eventually this should be re-enabled once the following issue is
resolved:

dotnet/runtime#79267
@rcdailey
Copy link

rcdailey commented Dec 7, 2022

is there a workaround that I can use to get this working?

If flipping EnableCompressionInSingleFile from true to false fixes the problem in your project, then that'd be the known workaround. 🙂

@am11 sadly, this did not work. Here is the build I tested:
https://github.com/recyclarr/recyclarr/actions/runs/3630695323

My project is entirely open source so you are welcome to experiment with it. I'm still looking for a workaround!

@agocke
Copy link
Member

agocke commented Dec 7, 2022

@VSadov for investigation

@agocke
Copy link
Member

agocke commented Dec 7, 2022

I would definitely take this for back port to 7

@agocke agocke removed the untriaged New issue has not been triaged by the area owner label Dec 7, 2022
@agocke agocke added this to the 7.0.x milestone Dec 7, 2022
@VSadov
Copy link
Member

VSadov commented Dec 8, 2022

Current theory is this is caused by intersection of compression and W^X features. The memory that we allocate for decompression needs to have special attributes (MAP_JIT), so that the protection could be flipped during relocations between writeable and executable.

@am11
Copy link
Member

am11 commented Dec 8, 2022

@am11 sadly, this did not work. Here is the build I tested: https://github.com/recyclarr/recyclarr/actions/runs/3630695323

@rcdailey, I was able to run that osx-arm64 recycler executable on M1 machine. There are few actions I needed to perform after downloading the zip:

# extract the app
$ unzip recyclarr-osx-arm64.zip

# remove apple quarantine attribute
$ xattr -d com.apple.quarantine recyclarr

# set executable bit
$ chmod +x ./recyclarr

# code sign it
$ codesign --force --deep -s - ./recyclarr

# run the app
$ ./recyclarr
recyclarr v3.0.1 (4.Branch.disable-compression.Sha.4ff513870d14a92aad8e68836fb155ddb525cf66)

USAGE
  recyclarr [options]
  recyclarr [command] [...]

OPTIONS
  -h|--help         Shows help text. 
  --version         Shows version information. 

COMMANDS
  create-config     Create a starter YAML configuration file 
  migrate           Perform any migration steps that may be needed between versions 
  radarr            Perform operations on a Radarr instance 
  sonarr            Perform operations on a Sonarr instance 

You can run `recyclarr [command] --help` to show help on a specific command.

It doesn't work when compression is enabled (e.g. if we download the artifacts from the current release https://github.com/recyclarr/recyclarr/releases/tag/v3.0.0 which doesn't have EnableCompressionInSingleFile=false fix).

@rcdailey
Copy link

rcdailey commented Dec 8, 2022

Thank you for checking @am11. Can you confirm that it does not work if you run without signing it and running the other steps? I'm pretty sure the users that reported the crash to me did not run those steps.

@am11
Copy link
Member

am11 commented Dec 8, 2022

Yup, I can confirm that without:

  • chmod step, the executable doesn't run
  • xattr step, the process immediately gets killed and we see this dialog:
    image
    • alternative way from UI is to open Finder, press-and-hold Control key and click Open the file to get a different dialog with Open button.
  • codesign step, the process immediately gets killed (and same thing happen under the lldb debugger, it hits sigkill from the OS even before hitting the native main which means we cannot see the backtrace)

Code-signing is mandatory on recent versions of macOS. dotnet-publish signs the binary when we are on macOS using codesign tool. When we publish macOS executable from Windows or Linux, this step gets skipped (because codesign is not in PATH). You can use macos-latest on GitHub actions (which is x64 only) and dotnet publish will sign both osx-x64 and osx-arm64 binaries. This way, codesign step won't be necessary on users's system.

Also, I think it's better to publish macOS executable from macOS machine and Linux from Linux machine, and create a .tar.xz package (tar cJf recyclarr-osx-arm64.tar.xz ./recyclarr) instead of .zip. This way, it will preserve executable permission after the extraction and both xattr and chmod steps won't be necessary on user's system.

@rcdailey
Copy link

rcdailey commented Dec 8, 2022

Thank you for those tips, I will make those changes.

In order to code sign my executable for MacOS, do I need to pay for an Apple developer subscription? Do I need to request a certificate from Apple in order to do that? I will try googling a bit, but my recollection from years ago is that this kind of thing required paying to be an apple developer. MacOS is not something I use so I'm mostly unfamiliar with their process.

@agocke
Copy link
Member

agocke commented Dec 8, 2022

I'm actually not super familiar with Apple's rules on this.

You can use macos-latest on GitHub actions (which is x64 only) and dotnet publish will sign both osx-x64 and osx-arm64 binaries. This way, codesign step won't be necessary on users's system.

@am11 does that really work? I thought ad-hoc signing only applied to the local machine -- if you tried to copy the binaries anywhere else they would have to be either re-signed or signed with an Apple developer cert.

@am11
Copy link
Member

am11 commented Dec 8, 2022

You are right; codesign step will still be needed on user's machine unless the app is notarized with these entitlements.

paying to be an apple developer

Yup, and it looks like a recurring cost; requires annual license renewal.

@rcdailey
Copy link

rcdailey commented Dec 12, 2022

@am11 I just remembered the reason why I'm cross-compiling from windows for all platforms:

#3828

This didn't make it into .NET 7, which is unfortunate. I'm blocked by that issue.

@am11
Copy link
Member

am11 commented Dec 12, 2022

#3828

That is another example of cross-OS publishing issue. I was suggesting to avoid cross-OS publishing altogether; cross-architecture publishing for same OS is fine.

On GitHub Actions it is possible by using dedicated jobs: runs-on: macos-latest to publish macOS artifacts, runs-on: windows-latest for Windows ones and libc-specific containers for Linux (ubuntu:latest for linux and alpine:latest for linux-musl).

@rcdailey
Copy link

So there was a time in the past where I did exactly what you're suggesting: A matrix build where on linux I build for linux, mac I build for mac, and windows I build for windows. However, I found that for linux / mac builds, no version information was in the executable unless I build them on Windows for those RIDs.

The reason I shared the issue was to show that there are reasons why it's difficult to do it the way you recommend as of current.

@vitek-karas
Copy link
Member Author

However, I found that for linux / mac builds, no version information was in the executable unless I build them on Windows for those RIDs.

I'm curious about that - AFAIK linux/mac doesn't have a common format to store version information in the executable. On Windows it's stored as a Win32 resource (and due to #3828 we're currently unable to write the Win32 resources on non-Windows machines). But on Linux/mac we've never written the version information anywhere. You will have a PE Win32 version resource in the managed .dll, but it won't be copied into the native executable. That behavior should be the same regardless if the build runs on Windows or not (if it targets for example linux RID).

@rcdailey
Copy link

I sadly don't know the inner workings of the whole thing. What I observed is that GitVersion / Nerdbank.GitVersioning libraries were not able to pull version information from the executable for my console application. That was a long time ago, though.

I'm revisiting the suggestion from @am11 to build on each platform so I'll see if I run into the issue again.

I apologize for bringing up an unrelated issue. It was not my intention to cause a distraction.

@rcdailey
Copy link

You are right; codesign step will still be needed on user's machine unless the app is notarized with these entitlements.

paying to be an apple developer

Yup, and it looks like a recurring cost; requires annual license renewal.

@am11 Are these instructions the guidance I should follow for signing my OSX application? It seems like a lot of work. I was hoping that dotnet publish did most of this for me; but I'm not sure. I am not able to find much official documentation on how to do this step by step.

@am11
Copy link
Member

am11 commented Dec 17, 2022

Are these instructions the guidance I should follow for signing my OSX application?

I haven't seen that link before, so I cannot say if/how that works. The simpler solution I was pointing to was to publish the macOS artifacts from macOS machine and pack it as a tar file (to preserve permission bits) and ask user to run codesign --force --deep -s - ./recyclarr after extracting the tarball on their system.

@rcdailey
Copy link

I wanted to sign the application via my GitHub workflow. I went ahead and purchased an apple developer membership so that I can do that. Seems like I need to do it by hand and dotnet tooling only does adhoc signing.

@am11
Copy link
Member

am11 commented Dec 18, 2022

Yes, the entire signing and notarization process is quite involved. Here is a flat script with raw steps I've pieced together for PublistSingleFile=true, you can fit it into your workflow YAML: https://gist.github.com/am11/924f18ea3a276e24390547da622290df

This is the .app version, you can also have .dmg, .pkg and .zip version to distribute signed software on macOS. This zip has to be created by ditto(1) utility which is usually present in PATH.

@rcdailey
Copy link

Is notarization required in order to distribute my executable to users without causing them trouble? The end goal, in my opinion, is for the application to execute normally as it would on Linux (which doesn't require all of this process).

The signing part seems easy; I just want to know if I can stop there or if I should do notarization as well. I do not plan to distribute my application via the Mac/Apple store.

@am11
Copy link
Member

am11 commented Dec 18, 2022

The end goal, in my opinion, is for the application to execute normally as it would on Linux (which doesn't require all of this process).

We don't need any of this process, not even signing, if that's your goal all along. Your app is fine: #79267 (comment)

@rcdailey
Copy link

I don't want my users to have to sign my application, my goal is to make my application ready to un-tarball and run with no extra fuss. Given that requirement, it's not really true that we don't need any of the process, right? If I'm wrong, where am I confused?

@am11
Copy link
Member

am11 commented Dec 18, 2022

You can use the posted script if you want app bundle (.app) model, then you don't need to manually zip or tar anything. You can skip notarization step 4 if you don't want "full trust".

I went ahead and purchased an apple developer membership so that I can do that.

Signing is a partial trust. You don't need to purchase anything for it, just sign up for a free developer certificate (Apple ID).

Notarization is full trust; Apple vouches for your app. That has a recurring cost.

So if you have purchased something and you don't need notarization, you can revert the transaction and get the reimbursement.

@rcdailey
Copy link

rcdailey commented Dec 18, 2022

The Developer ID certificate option was not even available on the developer apple website without a membership. Where am I supposed to go to get a "free developer certificate"? The only alternative I was able to find was signing with a self-signed cert, but that would not get rid of the prompts users see when they run my app.

Sorry for all the confusion on my part. I'm learning a lot, though, so thank you for this.

EDIT: Found some instruction here, but:

  1. Seems to require xcode (I don't have any mac hardware)
  2. Certificate seems to expire in 1 week which means maintenance burden unless the certificate renewal can be automated somehow

@agocke
Copy link
Member

agocke commented Dec 19, 2022

@am11 Are these instructions the guidance I should follow for signing my OSX application? It seems like a lot of work. I was hoping that dotnet publish did most of this for me; but I'm not sure. I am not able to find much official documentation on how to do this step by step.

Sorry, this is a rough edge at the moment. Unfortunately we don't really have any say in the Mac requirements. They are fully controlled by Apple. This is why we don't have any documentation on signing -- Apple is the ultimate authority on how to sign on their platform. The only documentation I have found from them is https://developer.apple.com/library/archive/documentation/Security/Conceptual/CodeSigningGuide/Procedures/Procedures.html

Similarly, this is why we don't do anything in dotnet publish itself. If Apple changes the requirements, which they have often done, dotnet publish may do the wrong thing. Apple releases do not necessarily align with dotnet releases so there is no guarantee that any fix would be available.

Lastly, we don't support signing on non-Apple platforms simply because Apple does not support signing on non-Apple platforms.

@rcdailey
Copy link

Thanks for all the support. I know I've taken this issue on quite a detour. At this point I've learned a lot and I will take further questions to the Apple dev forums. Thanks so much for everything and I apologize for going off topic.

@agocke
Copy link
Member

agocke commented Dec 19, 2022

Thanks for discussion! I know this is a sore spot, so I'm very interested in your feedback. Once you figure out what you need, I would love to know what, if anything, we could do to make this simpler without making promises that we can't keep (e.g., by supporting code signing on non-Apple platforms).

@am11
Copy link
Member

am11 commented Jan 9, 2023

This is resolved. The fix for EnableCompressionInSingleFile=true is available in .NET SDK 8 daily build (8.0.100-alpha.1.23059.8).

Backport to .NET 7: #80283.

@am11 am11 closed this as completed Jan 13, 2023
@ghost ghost locked as resolved and limited conversation to collaborators Feb 12, 2023
rcdailey added a commit to recyclarr/recyclarr that referenced this issue Apr 6, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants