safecall compile warning on macOS lazarus / 62-General ---------------------------------------------------------------------- davidfjenkins · 2024-02-05 20:04 I am building on intel mac with most recent OS.  I am using sevenzip.pas and JclCompression.pas to connect to the 7zip library.  The function declarations in sevenzip.pas and function callbacks in JclCompression use 'safecall'.  I get the following compiler warnings for the callback functions: JclCompression.pas(7716,39) Warning: Function result does not seem to be set the function is declared as:   TJclSevenzipExtractCallback = class(TJclSafecallInterfacedObject, I7zUnknown,     IProgress, IArchiveExtractCallback, ICryptoGetTextPassword,     ICompressProgressInfo)    public      procedure PrepareOperation(askExtractMode: Cardinal); safecall;    end; The function is: procedure TJclSevenzipExtractCallback.PrepareOperation(askExtractMode: Cardinal); begin {$IFDEF FPC} { If body is completely empty FPC doesn't put in safecall code} Self := Self {$ENDIF} end;  There are about 10 similar warnings for other callbacks.  Some have exception raising in them.  Some don't.  If I stick in a 'Result := 444'. The compilation fails.  Is there a way to set a result value and resolve the warning? There is also a broader question about safecall on unix (macOS/Linux).  I have -gt set for my debug compiles and the stack is filled with $55555555.  For the safecall callbacks, I am seeing $55555555 come back as a result.  Looking at the assembly I see that a try/except has been set up for the callback.  And then at the bottom if no exception occurs then $eax is set from uninitialized stack and is set to $55555555.  This causes a problem when I come back from an 'open' call and have a value of $55555555 as the return value. - Should safecall handle return values fine on macOS/Unix even with -gt enabled? - Is there a way to get the return value passed through correctly? - Do I just need to replace 'safecall' declarations with function foo(): HResult; stdcall; Thanks David ---------------------------------------------------------------------- Thaddy · 2024-02-05 20:07 Your code would probably also fail on Windows and other platforms. You do not declare function but procedure. ---------------------------------------------------------------------- Jonas Maebe · 2024-02-05 21:51 safecall support currently doesn't seem to be enabled on macOS. It could be enabled by adding tf_safecall_exceptions to the flag of the appropriate target definitions in compiler/systems/i_darwin.pas, but I'd have to test first how well it works (it should be fine since the code should be generic, but you never know). @Thaddy: a safecall procedure is internally transformed into a function that returns a hresult, which is zero if no exception occurred and non-zero if one did occur. There is nothing wrong with the original code. ---------------------------------------------------------------------- davidfjenkins · 2024-02-06 16:57 Thanks Jonas. Turns out adding tf_safecall_exceptions does mostly work in macOS.  I had forgotten that we had added that flag in our local compiler/systems/i_darwin.pas when we first added 7zip support to Beyond Compare.  Looking at the resulting assembly code I can see all the wrappers and safecall exception code being added. For a call to an external safecall procedure, there is a call added to check the return value and throw an exception if it is negative. For a call from an external library to a local procedure that is declared as safecall, I can see that try/except wrapper is added and that if an exception occurs it is passed back in eax.  So definitely the basics are there.  There are two issues that I am seeing but they show up on linux as well and the tf_safecall_exceptions flag is already set in linux - not something we added. 1) The warning about a function result not being set in a procedure declared as safecall And 2) If -gt is enabled, the trashing local variables code and try/except wrapper code collide. Here is an assembly chunk from x86_64 intel compiled on macOS that shows that: BCompare`SEEK:     0x100ab7070 <+0>:   pushq  %rbp     0x100ab7071 <+1>:   movq   %rsp, %rbp     0x100ab7074 <+4>:   leaq   -0xc0(%rsp), %rsp     0x100ab707c <+12>:  movq   %rbx, -0xb8(%rbp)     0x100ab7083 <+19>:  movq   %r12, -0xb0(%rbp)     0x100ab708a <+26>:  movq   %r13, -0xa8(%rbp)     0x100ab7091 <+33>:  movq   %r14, -0xa0(%rbp)     0x100ab7098 <+40>:  movq   %r15, -0x98(%rbp)     0x100ab709f <+47>:  movq   %rdi, -0x20(%rbp)     0x100ab70a3 <+51>:  movq   %rsi, -0x8(%rbp)     0x100ab70a7 <+55>:  movl   %edx, -0x10(%rbp)     0x100ab70aa <+58>:  movq   %rcx, -0x18(%rbp)     0x100ab70ae <+62>:  movq   $0xb8, %rdi     0x100ab70b5 <+69>:  callq  0x10001bd40               ; fpc_stackcheck at system.inc:1060:1     0x100ab70ba <+74>:  leaq   -0x48(%rbp), %rdx     0x100ab70be <+78>:  leaq   -0x88(%rbp), %rsi     0x100ab70c5 <+85>:  movl   $0x1, %edi     0x100ab70ca <+90>:  callq  0x100017e10               ; fpc_pushexceptaddr at except.inc:84:1     0x100ab70cf <+95>:  movq   %rax, %rdi     0x100ab70d2 <+98>:  callq  0x100005340               ; fpc_setjmp at setjump.inc:48:5     0x100ab70d7 <+103>: movl   %eax, -0x8c(%rbp)     0x100ab70dd <+109>: cmpl   $0x0, %eax     0x100ab70e0 <+112>: jne    0x100ab715d               ; <+237> at JclCompression.pas     0x100ab70e2 <+114>: movl   $0x0, -0x24(%rbp)     0x100ab70e9 <+121>: movl   $0xaaaaaaaa, -0x24(%rbp)  ; imm = 0xAAAAAAAA     0x100ab70f0 <+128>: movl   $0xaaaaaaaa, -0x30(%rbp)  ; imm = 0xAAAAAAAA     0x100ab70f7 <+135>: movl   $0xaaaaaaaa, -0x2c(%rbp)  ; imm = 0xAAAAAAAA ->  middle code removed 0x100ab715d <+237>: callq  0x100018150               ; fpc_popaddrstack at except.inc:203:1     0x100ab7162 <+242>: movl   -0x8c(%rbp), %eax     0x100ab7168 <+248>: cmpl   $0x0, %eax     0x100ab716b <+251>: je     0x100ab7187               ; <+279> at JclCompression.pas:6097:1     0x100ab716d <+253>: movq   -0x20(%rbp), %rdi     0x100ab7171 <+257>: callq  0x100018470               ; fpc_safecallhandler at except.inc:365:1     0x100ab7176 <+262>: movl   %eax, -0x24(%rbp)     0x100ab7179 <+265>: jmp    0x100ab7187               ; <+279> at JclCompression.pas:6097:1     0x100ab717b <+267>: movl   $0x0, -0x8c(%rbp)     0x100ab7185 <+277>: jmp    0x100ab715d               ; <+237> at JclCompression.pas     0x100ab7187 <+279>: movslq -0x24(%rbp), %rax     0x100ab718b <+283>: movq   -0xb8(%rbp), %rbx     0x100ab7192 <+290>: movq   -0xb0(%rbp), %r12     0x100ab7199 <+297>: movq   -0xa8(%rbp), %r13     0x100ab71a0 <+304>: movq   -0xa0(%rbp), %r14     0x100ab71a7 <+311>: movq   -0x98(%rbp), %r15     0x100ab71ae <+318>: movq   %rbp, %rsp     0x100ab71b1 <+321>: popq   %rbp     0x100ab71b2 <+322>: retq Notice at the top that the result is going to be stored in $0x24(%rbp) and that location is set to 0.  But immediately after i$0x24(rbp)  is set to the trash value, 0xAAAAAAAA.   So at the end when there was no exception to catch, the code jumps to +279 and grabs the value stored in 0x24(%bp) and places it in %eax.  The result is that it looks like an exception did occur. Here is the top portion of the same function compiled for x86_64 on linux: BCompare`SEEK:     0xed7740 <+0>:   pushq  %rbp     0xed7741 <+1>:   movq   %rsp, %rbp     0xed7744 <+4>:   leaq   -0xc0(%rsp), %rsp     0xed774c <+12>:  movq   %rbx, -0xb8(%rbp)     0xed7753 <+19>:  movq   %r12, -0xb0(%rbp)     0xed775a <+26>:  movq   %r13, -0xa8(%rbp)     0xed7761 <+33>:  movq   %r14, -0xa0(%rbp)     0xed7768 <+40>:  movq   %r15, -0x98(%rbp)     0xed776f <+47>:  movq   %rdi, -0x20(%rbp)     0xed7773 <+51>:  movq   %rsi, -0x8(%rbp)     0xed7777 <+55>:  movl   %edx, -0x10(%rbp)     0xed777a <+58>:  movq   %rcx, -0x18(%rbp)     0xed777e <+62>:  movabsq $0xb8, %rdi     0xed7788 <+72>:  callq  0x43fee0                  ; fpc_stackcheck at system.inc:1060     0xed778d <+77>:  leaq   -0x48(%rbp), %rdx     0xed7791 <+81>:  leaq   -0x88(%rbp), %rsi     0xed7798 <+88>:  movl   $0x1, %edi     0xed779d <+93>:  callq  0x43c070                  ; fpc_pushexceptaddr at except.inc:84     0xed77a2 <+98>:  movq   %rax, %rdi     0xed77a5 <+101>: callq  0x429d00                  ; fpc_setjmp at setjump.inc:48     0xed77aa <+106>: movl   %eax, -0x8c(%rbp)     0xed77b0 <+112>: cmpl   $0x0, %eax     0xed77b3 <+115>: jne    0xed7834                  ; <+244> at JclCompression.pas     0xed77b9 <+121>: movl   $0x0, -0x24(%rbp)     0xed77c0 <+128>: movl   $0x55555555, -0x24(%rbp)  ; imm = 0x55555555     0xed77c7 <+135>: movl   $0x55555555, -0x30(%rbp)  ; imm = 0x55555555     0xed77ce <+142>: movl   $0x55555555, -0x2c(%rbp)  ; imm = 0x55555555 This isn't critical to what we are doing.  I can ignore the warnings and we only have -gt turned on for development and I can turn it off.  I tried to look back at the commit where we added tf_safecall_exceptions for macOS to see why we did it.  We switched up repositories after that change (it was several years ago) and I couldn't find a commit message.  I believe that it was done so that we could compile with all the 'safecalls' in the Jcl and P7Zip code that we added without having to change all of them. If the trash variable code and safecall code collision looks like a bug to you I can enter a freepascal bug report for it. ---------------------------------------------------------------------- PascalDragon · 2024-02-08 20:49 Quote from: davidfjenkins on February 06, 2024, 05:57:10 pmHere is an assembly chunk from x86_64 intel compiled on macOS that shows that: Please use [code][/code]-tags for better viewability.