External .LIB compiles with ASM Backend, but not C Backend

Just starting out? Need help? Post your questions and find answers here.
JoeC4281
User
User
Posts: 32
Joined: Fri Aug 06, 2021 4:47 pm

External .LIB compiles with ASM Backend, but not C Backend

Post by JoeC4281 »

I am using;

Code: Select all

PureBasic 6.00 LTS (Windows - x64)
Windows 10 [Version 10.0.19044.3086]
I have a library, TakeCmd.lib

I want to use Command from the TakeCmd.lib (yes, it is actually called Command)

Code: Select all

dumpbin /exports takecmd.lib
shows me

Code: Select all

?Command@@YAHPEB_WH@Z (int __cdecl Command(wchar_t const *,int))
TakeCmd.h which shows me

Code: Select all

int WINAPI Command( LPCTSTR pszLine, int nReserved );
/* 
	Call the parser to expand and execute pszLine
	nReserved - reserved, must be set to 0
which I then declare as an external function from TakeCmd.lib

Code: Select all

ImportC "takecmd.lib"
  Command_(*lpszString, nReserved.i) As "?Command@@YAHPEB_WH@Z"
EndImport
Executing the command;

Code: Select all

Command_("pause", 0)
compiles, and works, using pbcompiler.exe (assembler backend)

However, using pbcompilerc.exe (C backend), it does not compile, and returns 619 lines of errors;

Code: Select all

PureBasic 6.00 LTS - C Backend (Windows - x64)
Loading 'C' subsystem
Compiling ActiveScript.pb
Loading external libraries...
Starting compilation...
Including source: Modul_ActiveScript.pb
1877 lines processed.
Error: Assembler
purebasic.c: In function 'activescriptsiteXf_onscripterror':
purebasic.c:865:16: warning: passing argument 1 of 'SYS_CopyString' discards 'volatile' qualifier from pointer target type [-Wdiscarded-qualifiers]
 SYS_CopyString(v_error);
                ^~~~~~~
.
.
.
purebasic.c:231:43: note: expected 'TCHAR *' {aka 'void *'} but argument is of type 'volatile void *'
 M_SYSFUNCTION(void) SYS_FreeString(TCHAR *String);
                                    ~~~~~~~^~~~~~
C:\Users\jlcav\AppData\Local\Temp\ccF8Sg8s.s: Assembler messages:

C:\Users\jlcav\AppData\Local\Temp\ccF8Sg8s.s:3295: Error: invalid character '?' before operand 1
I'm okay with it compiling and working with the ASM Backend,
however,
after much time trying to figure out what was wrong in my code,
using the C Backend,
and finding nothing wrong with my code,
using the C Backend,
it was then that I thought to try compiling with the ASM Backend,
which works as it should.

Constructive suggestions as to why this code compiles with the ASM Backend,
but does not compile with the C Backend.

Joe
fryquez
Enthusiast
Enthusiast
Posts: 391
Joined: Mon Dec 21, 2015 8:12 pm

Re: External .LIB compiles with ASM Backend, but not C Backend

Post by fryquez »

PureBasic 6.00 LTS is bit old, have you tried latest beta of 6.03?
juergenkulow
Enthusiast
Enthusiast
Posts: 581
Joined: Wed Sep 25, 2019 10:18 am

Re: External .LIB compiles with ASM Backend, but not C Backend

Post by juergenkulow »

Can you post from the file C:\Users\jlcav\AppData\Local\Temp\ccF8Sg8s.s the line 3295 and the previous lines up to and including the line that starts with #?
JoeC4281
User
User
Posts: 32
Joined: Fri Aug 06, 2021 4:47 pm

Re: External .LIB compiles with ASM Backend, but not C Backend

Post by JoeC4281 »

I have updated to PureBasic 6.02 LTS (Windows - x64)

Not comfortable with updating to Beta 6.03 at this time.

When compiling with the C Backend,
the number of errors have been reduced to;

Code: Select all

PureBasic 6.02 LTS - C Backend (Windows - x64)
Loading 'C' subsystem
Compiling ActiveScript.pb
Loading external libraries...
Starting compilation...
Including source: Modul_ActiveScript.pb
1880 lines processed.
Error: Assembler
r:\temp\ccI90jLb.s: Assembler messages:
r:\temp\ccI90jLb.s:2890: Error: invalid character '?' before operand 1
Compiling with the ASM Backend results in successful compilation and execution of program.

Joe
Olli
Addict
Addict
Posts: 1200
Joined: Wed May 27, 2020 12:26 pm

Re: External .LIB compiles with ASM Backend, but not C Backend

Post by Olli »

@Joe

I can read this message :
juergenkulow wrote:Can you post from the file C:\Users\jlcav\AppData\Local\Temp\ccF8Sg8s.s the line 3295 and the previous lines up to and including the line that starts with #?
I agree this question relates to the version 6.00.

But could even bring the info requested by juergenkulow ?

I observe juergenkulow asks very accurate questions in this domain (in this subject and in a recent subject which is near).

If you, or an other member who has the same problem, you do not give accurate answers to such accurate questions, then your problems will be hardly solvable.

A technical remark I can bring, is the problem is not only for one compiler, but for all compilers (Microsoft as Linux). They have different standards (more than 12, but a main part has open source solves, and, on Microsoft, it is automated by a specific function). So lots of intereting subjects will appear to determine which best converting will be choosen.
Olli
Addict
Addict
Posts: 1200
Joined: Wed May 27, 2020 12:26 pm

Re: External .LIB compiles with ASM Backend, but not C Backend

Post by Olli »

@fred

On Microsoft, the option mask can be pre-defined.
Ms french doc
JoeC4281
User
User
Posts: 32
Joined: Fri Aug 06, 2021 4:47 pm

Re: External .LIB compiles with ASM Backend, but not C Backend

Post by JoeC4281 »

The C:\Users\jlcav\AppData\Local\Temp\ccF8Sg8s.s file does not exist after compilation.

The r:\temp\ccI90jLb.s file does not exist after compilation.

How do I stop the files from being deleted after compilation?

Joe
juergenkulow
Enthusiast
Enthusiast
Posts: 581
Joined: Wed Sep 25, 2019 10:18 am

Re: External .LIB compiles with ASM Backend, but not C Backend

Post by juergenkulow »

C Backend AT&T Asm cc*.s

Code: Select all

	.loc 1 328 12
	movl	$__S1, %eax
	movl	$0, 4(%esp)
	movl	%eax, (%esp)
	call	?Command@@YAHPEB_WH@Z
	        ^
ASM Backend FASM

Code: Select all

; Command_("pause", 0)
  PUSH   dword 0
  MOV    eax,_S1
  PUSH   eax
  CALL   ?Command@@YAHPEB_WH@Z
  ADD    esp,8
JoeC4281
User
User
Posts: 32
Joined: Fri Aug 06, 2021 4:47 pm

Re: External .LIB compiles with ASM Backend, but not C Backend

Post by JoeC4281 »

Using the Take Command Console Foldermonitor command,
I was able to isolate the problem file.

Code: Select all

r:\temp\ccqAtEwh.s: Assembler messages:
r:\temp\ccqAtEwh.s:2894: Error: invalid character '?' before operand 1
Line 2894 is call ?Command@@YAHPEB_WH@Z

Code: Select all

R:\hold>type /l r:\hold\ccqAtEwh.s | tail /n+2890
2891 :  movq    -24(%rbp), %rax
2892 :  movl    $0, %edx
2893 :  movq    %rax, %rcx
2894 :  call    ?Command@@YAHPEB_WH@Z
2895 :  movq    %rax, -32(%rbp)
2896 :  call    SYS_PopStringBasePositionUpdate
2897 :  movq    $0, -8(%rbp)
2898 :  nop
2899 : .L158:
2900 :  movq    -8(%rbp), %rax
Similar to what juergenkulow posted.

Is there a solution for the C Backend?

Again,
everything compiles and works with the Assembler backend,
which is fine with me.

Joe

Notes for my future reference:
Ref: Foldermonitor https://jpsoft.com/help/foldermonitor.htm

Code: Select all

foldermonitor r:\temp created modified forever copy "r:\temp\%%_folderfile1" "r:\hold"
User avatar
yuki
Enthusiast
Enthusiast
Posts: 101
Joined: Sat Mar 31, 2018 9:09 pm

Re: External .LIB compiles with ASM Backend, but not C Backend

Post by yuki »

JoeC4281 wrote: Thu Sep 28, 2023 1:21 am Is there a solution for the C Backend?

Again,
everything compiles and works with the Assembler backend,
which is fine with me.
You'd likely get away with loading from the associated DLL directly:

Code: Select all

DeclareModule TakeCmd : EnableExplicit
  
  Declare Command(line.s)
  
EndDeclareModule

Module TakeCmd : EnableExplicit
  
  ; Private types.
  ; ═══════════════════════════════════════════════════════
  PrototypeC _p_libTakeCmd_Command(line.p-unicode, reserved.i)
  
  ; Private state.
  ; ═══════════════════════════════════════════════════════
  Global _g_Command._p_libTakeCmd_Command
  
  ; Private functions.
  ; ═══════════════════════════════════════════════════════
  Procedure _initLib()
    Protected libTakeCmd = OpenLibrary(#PB_Any, "TakeCmd.dll")
    If Not libTakeCmd
      ProcedureReturn #False
    EndIf
    
    _g_Command = GetFunction(libTakeCmd, "?Command@@YAHPEB_WH@Z")
    
    ; If you've other functions to load, change to, e.g.:
    ;   _g_Command And _g_AnotherFn And _g_YetAnotherFn ...
    ; Just make sure all your functions are loaded.
    ProcedureReturn _g_Command
  EndProcedure
  
  ; Public functions.
  ; ═══════════════════════════════════════════════════════
  Procedure Command(line.s)
    ProcedureReturn _g_Command(line, #Null)
  EndProcedure
  
  ; Initialisation.
  ; ═══════════════════════════════════════════════════════
  If Not _initLib()
    MessageRequester("Fatal Error:", "Failed to load TakeCmd library.")
    End
  EndIf
  
EndModule

TakeCmd::Command("pause")
I've taken the liberty of wrapping it up in a module, which may be handy to contain other helpers for working with the library. You're of course free to forego that and make the prototype and function simply global.
JoeC4281
User
User
Posts: 32
Joined: Fri Aug 06, 2021 4:47 pm

Re: External .LIB compiles with ASM Backend, but not C Backend

Post by JoeC4281 »

Thankyou for that Yuki.

Unfortunately, that will not work.

I did try your method, though, just to confirm.

I use PureBasic to create plugins for Take Command Console,
Ref: https://jpsoft.com/all-downloads/tcc-plugins.html,
which means I cannot LoadLibrary and GetFunction the TakeCmd.dll,
even from within a plugin.

Ref: https://www.jpsoft.com/forums/threads/t ... post-62193
If you're trying to load TakeCmd.dll w/o TCMD or TCC that won't work; they both do some required initialization for TakeCmd.dll's internal variables and structures.
Ref: viewtopic.php?p=575642#p575642
Error when using TakeCmd.lib
Joe
User avatar
idle
Always Here
Always Here
Posts: 5836
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: External .LIB compiles with ASM Backend, but not C Backend

Post by idle »

The problem with the c backend is the import with the mangled name. I don't know how you alias in c but it's passing the import on to as that is causing the problem

Code: Select all

ImportC "takecmd.lib"
  Command_(*lpszString, nReserved.i) As "?Command@@YAHPEB_WH@Z"
EndImport
Results in something like this which is valid c but AS goesnt like it.

Code: Select all

asm(?Command@@YAHPEB_WH@Z)   
In c you'd declare the function as extern __cdecl ...

I think it's a bug.
Olli
Addict
Addict
Posts: 1200
Joined: Wed May 27, 2020 12:26 pm

Re: External .LIB compiles with ASM Backend, but not C Backend

Post by Olli »

juergenkulow wrote: Wed Sep 27, 2023 4:41 pm C Backend AT&T Asm cc*.s

Code: Select all

	.loc 1 328 12
	movl	$__S1, %eax
	movl	$0, 4(%esp)
	movl	%eax, (%esp)
	call	?Command@@YAHPEB_WH@Z
	        ^
ASM Backend FASM

Code: Select all

; Command_("pause", 0)
  PUSH   dword 0
  MOV    eax,_S1
  PUSH   eax
  CALL   ?Command@@YAHPEB_WH@Z
  ADD    esp,8
Hello juergenkulow,

initially, I thank that joe would answer... But you did it. Thank you for your effort. It seems that it maybe is missing a mangled name converter in the C backend. But... When I read this :

Code: Select all

	.loc 1 328 12
	movl	$__S1, %eax
	movl	$0, 4(%esp)
	movl	%eax, (%esp)
	call	?Command@@YAHPEB_WH@Z
	        ^
... what is this language ? Source and destination are swapped... ? The shifting address has an other syntax. And the stack is managed in the opposite time (bound before, and positive store index versus bound after and negative store index.

After these strange differences, I think (to be confirmed...) a pre-processing level should be added to convert ms mangled name to single name. I go a little bit too much forward, but maybe a simple trim of '?' character and the suffix should be required if '?'.
User avatar
yuki
Enthusiast
Enthusiast
Posts: 101
Joined: Sat Mar 31, 2018 9:09 pm

Re: External .LIB compiles with ASM Backend, but not C Backend

Post by yuki »

JoeC4281 wrote: Thu Sep 28, 2023 6:27 pm Thankyou for that Yuki.

Unfortunately, that will not work.

I did try your method, though, just to confirm.

I use PureBasic to create plugins for Take Command Console,
Ref: https://jpsoft.com/all-downloads/tcc-plugins.html,
which means I cannot LoadLibrary and GetFunction the TakeCmd.dll,
even from within a plugin.
Ah, thanks for the added context, that is handy!

From a DLL, it's generally a bad idea to load other DLLs inside DllMain, which is what the previous example would do when compiled in this way. It also seems like TakeCmd.dll lacks a "?Command@@YAHPEB_WH@Z" and instead exports simply "Command".

I tested the following with TCMD 31 and it seems to work well in both ASM and C-backends, defining a new "thingy" command:

Code: Select all

DeclareModule TakeCmd : EnableExplicit
  
  ; Structures.
  ; ═══════════════════════════════════════════════════════
  
  Structure PluginVersionInfo
    major.l
    minor.l
    build.l
  EndStructure
  
  Structure PluginAuthorInfo
    name.s
    email.s
    url.s
  EndStructure
  
  Structure PluginInfo
    dllName.s
    author.PluginAuthorInfo
    description.s
    functions.s
    version.PluginVersionInfo
    moduleHandle.i
    moduleName.s
  EndStructure
  
  ; Functions: init.
  ; ═══════════════════════════════════════════════════════
  
  ; Loads TakeCmd library functions. Must be called once at
  ; startup before executing other functions.
  Declare InitializeLibrary()
  
  ; Functions: parser.
  ; ═══════════════════════════════════════════════════════
  
  ; Calls the parser to expand and execute the given line.
  Declare.l Command(line.s)
  
  ; Functions: error handling.
  ; ═══════════════════════════════════════════════════════
  
  ; Beeps.
  Declare Honk()
  
EndDeclareModule

Module TakeCmd : EnableExplicit
  
  ; Private types.
  ; ═══════════════════════════════════════════════════════
  Prototype _p_libTakeCmd_Command(line.p-unicode, reserved.i)
  Prototype _p_libTakeCmd_honk()
  
  ; Private state.
  ; ═══════════════════════════════════════════════════════
  Global _g_Command._p_libTakeCmd_Command
  Global _g_honk._p_libTakeCmd_honk
  
  ; Public functions: init.
  ; ═══════════════════════════════════════════════════════
  
  Procedure InitializeLibrary()
    Static isInitialised, libTakeCmd
    If isInitialised
      ; Already initialised OK previously. Skip reattempts.
      ProcedureReturn #True
    ElseIf libTakeCmd
      ; Already failed initialisation previously. Skip reattempts.
      ProcedureReturn #False
    EndIf
    
    libTakeCmd = OpenLibrary(#PB_Any, "TakeCmd.dll")
    If Not libTakeCmd
      ProcedureReturn #False
    EndIf
    
    _g_Command  = GetFunction(libTakeCmd, "Command")
    _g_honk     = GetFunction(libTakeCmd, "honk")
    
    isInitialised = Bool(_g_Command And _g_honk)
    
    ProcedureReturn isInitialised
  EndProcedure
  
  ; Public functions: parser.
  ; ═══════════════════════════════════════════════════════
  
  Procedure.l Command(line.s)
    ProcedureReturn _g_Command(line, #Null)
  EndProcedure
  
  ; Public functions: error handling.
  ; ═══════════════════════════════════════════════════════
  
  Procedure Honk()
    ProcedureReturn _g_honk()
  EndProcedure
  
EndModule

ProcedureDLL InitializePlugin()
  If Not TakeCmd::InitializeLibrary()
    MessageRequester("Error:",
                     "Failed to load <PLUGIN_NAME> plugin!" + #CRLF$ +
                     "Please try and install the latest version from <AUTHOR_URL> or contact support at <AUTHOR_EMAIL>.",
                     #PB_MessageRequester_Error)
    ProcedureReturn 1
  EndIf
  ProcedureReturn 0
EndProcedure

ProcedureDLL GetPluginInfo()
  Static pluginInfo.TakeCmd::PluginInfo
  
  With pluginInfo
    \dllName      = "Example"
    \description  = "Does a thingy."
    \functions    = "thingy"
    \author\name  = "Someone"
    \author\email = "someone@somewhere.com"
    \author\url   = "somewhere.com"
    \version\major  = 1
    \version\minor  = 0
    \version\build  = 0
  EndWith
  
  ProcedureReturn @pluginInfo
EndProcedure

ProcedureDLL ShutdownPlugin()
  ProcedureReturn 0
EndProcedure

ProcedureDLL thingy(*argsRaw.Unicode)
  ; Take ping params as input, defaulting to "purebasic.com"
  Protected pingParams.s = Trim(PeekS(*argsRaw, -1, #PB_Unicode))
  If pingParams = ""
    pingParams = "purebasic.com"
  EndIf
  
  TakeCmd::Honk()
  TakeCmd::Command("ping " + pingParams)
  TakeCmd::Honk()
  TakeCmd::Honk()
  
  ProcedureReturn 0
EndProcedure
This is only a workaround for what looks like a compiler bug. Unless the C-backend is required for your project, I might suggest using simply ASM with your existing working approach for now.
JoeC4281
User
User
Posts: 32
Joined: Fri Aug 06, 2021 4:47 pm

Re: External .LIB compiles with ASM Backend, but not C Backend

Post by JoeC4281 »

yuki,
thankyou for taking the time to produce this source code,
which is an alternate method of plugin coding for Take Command Console using PureBasic.

I tried your code, and it works as expected.

Questions.

Using your code, I no longer require the use of;

Code: Select all

ImportC "takecmd.lib"
which means that I no longer require the existence of a takecmd.lib file for the plugin.

What am I gaining/losing by using your coding method, that is, OpenLibrary and GetFunction, versus having to use the takecmd.lib library?

I'm liking the way you coded the plugin, versus the way I have been coding the plugin.

I will do more reading on DeclareModule/EndDeclareModule and Module/EndModule, as these are new to me.

Thanks again for your efforts.
Much appreciated.

Joe
Post Reply