Over the moon with being able to use masm in it's own environment for my stuff...
I've been looking at making a start and researching the best way to process an array of strings passed by c++ to a masm function. It's nagging me that in the example masm function that I've used so far the arguments appear as DWORDS in masm but are floats in C++? I fear I'm missing some rules of thumb here.
Re the strings. At present they're in a TStringList...which is a class. I have no idea how they're represented and am only familiar with PB's dynamic strings and null terminated strings which I am quite happy to convert the TStringList to.
To my simple mind it seems a good idea to pass the string array as an address to a lump of memory structured as follows
number of strings 4 bytes
len of 1st string string 4 bytes
1st string n bytes
len of 2nd string 4 bytes
2nd string n bytes
etc
I am aware that the intel microprocessors have all sorts of built-in short cuts for doing stuff and don't want to miss a trick. Therefore...could some kind soul put me straight on the most straight-forward way to go about this.
BTW the strings are going to be language tokens...that I need to convert to some sort of routine addresses.
I don't know if this extra information has any bearing on the approach.
Any guidance much appreciated.
Hi bobl,
passing an array to an assembly language procedure isn't so hard: pass a pointer to the first array element and the array length. That's all what your function needs.
Quote from: bobl on April 27, 2013, 10:51:15 PM
Re the strings. At present they're in a TStringList...which is a class.
That's the real problem. Writing external assembly language functions which operate on class members isn't easy. I wouldn't do that. If I can remember it right, Agner Fog (http://www.agner.org/optimize/) describes that complicated technique in one of the manuals. The other way is: let the compiler do the dirty work and use the inline assembler.
Gunther
Can you post your C++ code with a sample array? I'm also curious how to do that :biggrin:
Just for fun, I googled string array ansi C - strings seem to be hilariously clumsy in C... ::)
I've just got a richedit box that I type the tokens into...delimited by space.
I was splittling them and putting them into a listbox....
and will again but I've stripped it out for now to process them in turn with Outer() in class cForth in unit2.
In the richedit box you need to type a couple of words in to see it in action.
Hope that helps
here's unit1.cpp
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#include "Unit2.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
#define CR1 13
#define CR2 10
#define SPC 32
//remember to delete this
//you have to use new to create it
//TStringList * tkns = new TStringList;
void __fastcall TForm1::Button1Click(TObject *Sender)
{
ShowMessage ("hi");
//split program into tokens and put in listbox
AnsiString src = RichEdit1->Lines->Text;
char * pB = src.c_str();
AnsiString buf = "";
TStringList * tkns = new TStringList;
for ( int i = 0; i < src.Length(); i++ ) {
switch(*pB){
case CR1 :
if (*(pB+1) == CR2){
tkns->Add(buf);
//ListBox1->Items->Add(buf);
buf = "";
pB++;
}
break;
case SPC :
tkns->Add(buf);
//ListBox1->Items->Add(buf);
buf = "";
break;
default :
buf = buf + *pB;
}
pB++;
}
//How to see an individual string
//ShowMessage( tkns->Strings[1] );
cForth f;
f.Outer(tkns);
delete tkns;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormKeyPress(TObject *Sender, char &Key)
{
//only works if form's keypreview is set to true
//Key must be with capital K in c builder
switch (Key) {
case 27 : //esc
Close();
//don't trap else can't type stuff in richtext
// default:
// ShowMessage("don't know what key you pressed in TForm1::FormKeyPress");
}
}
//---------------------------------------------------------------------------
which uses unit2.cpp
//---------------------------------------------------------------------------
#pragma hdrstop
#include "Unit2.h"
#define CR1 13
#define CR2 10
#define SPC 32
cForth::cForth(){
Init();
}
void cForth::Init(){
ShowMessage("doing Init");
/*
char * p = src.c_str(); //c_str() returns byte array addr
for ( int i = 0; i < src.Length(); i++ ) {
if ( (*p == CR1) && (*(p+1) == CR2) ){
ListBox1->Items->Add("<CR>detected");
}
else if (*p == SPC){ListBox1->Items->Add("<SPC>detected");}
else {ListBox1->Items->Add(*p);}
p++;
}
*/
}
//only passing one in at a time means step
void cForth::Outer(TStringList * tkns){
for (int i = 0; i <= tkns->Count-1; i++) {
ShowMessage(tkns->Strings[i]);
}
}
unit1.h
//---------------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ComCtrls.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
TButton *Button1;
TRichEdit *RichEdit1;
TRichEdit *RichEdit2;
void __fastcall Button1Click(TObject *Sender);
void __fastcall FormKeyPress(TObject *Sender, char &Key);
private: // User declarations
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
unit2.h
#include "system.hpp" //for AnsiString
#include "Classes.hpp" //for TStringList
#include "Unit1.h"
#include "Dialogs.hpp"
class cForth {
public:
static int pc;
static int sub_pc;
cForth();
~cForth(){};
void Init();
void Outer(TStringList *);
};
//---------------------------------------------------------------------------
#ifndef Unit2H
#define Unit2H
//---------------------------------------------------------------------------
#endif
project1.cpp
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
//---------------------------------------------------------------------------
USEFORM("Unit1.cpp", Form1);
//---------------------------------------------------------------------------
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
try
{
Application->Initialize();
Application->CreateForm(__classid(TForm1), &Form1);
Application->Run();
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
catch (...)
{
try
{
throw Exception("");
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
}
return 0;
}
//--------------
If you need to pass a string array, you do it best by passing an array of pointers. If the first element of the array is the string count followed by a pointer to each string up to the number of items in the array. With a bit of algo design you can make this a single memory allocation and it will occur something like this.
count,pointer1,pointer2,pointer3, ---- item1,0,item2,0,item3,0 etc ....
You just pass the address of the array and at the receivers end you read the count and use that number of pointers to the array members.
Hutch's solution is fine, of course, but it requires some work on the C++ end. I wonder if C++ maintains already a memory block with these pointers...
Could you zip up that project folder and attach it here? Preferably with an assembler stub that is passed a pointer to string element 0?
Hutch
Thanks for your advice...I'll try that.
I've been reading around strlen type functions in case null-terminated strings is the way to go.
With that sort of array it sounds like it might be.
I'll come back when I've got something to show.
jj207
Yes. No problem. Please bear with me.
Keep the pointers together, so you're not jumping around in memory just to find what you want -- which would spoil the cache. Alignment is the other issue; not mixing data type sizes helps with this.
Also, unless you expect tokens to be particularly long, 1 byte should be fine for the length (up to 255 chars.)
I'd probably go with something like:
numStrings DWORD
pString1 DWORD
pString2 DWORD
...
pStringN DWORD
0 DWORD ;null pointer - indicates the end of the list
lenString1 BYTE ;length of string in characters (actual byte length is +1)
String1 CHAR[] ;zero-terminated to be used with C
lenString2 BYTE
String2 CHAR[]
...
lenStringN BYTE
StringN CHAR[]
Tedd
Thanks very much for your post
jj2007
by asm stub...do you mean incorporate the obj of this and supply the first TStringList substring
.586p
.model flat
asmstub PROTO C Arg1:DWORD
.CODE
asmstub PROC C Arg1:DWORD
;what do you want in here?
asmstub ENDP
END
Quote from: bobl on April 28, 2013, 12:18:49 AM
asmstub PROC C Arg1:DWORD
;what do you want in here?
asmstub ENDP
A pointer to the first string in the array. But the stub is not the problem, the C++ code will be. That's not my strong point - most of the time my blood pressure rises when VC++ throws (an array of) errors at me. Pelles C is a bit gentler but no ++ there, it's plain ANSI C...
I'm working on it but have to go and pick someone up.
Back soon
Jochen, Steve, bobl,
the problem isn't the assembly language code or how to pass the array from C++ to the procedure. The tricky part is that:
Quote
Classes are coded as structures in assembly and member functions are coded as functions that receive a pointer to the class/structure as a parameter. It is not possible to apply the extern "C" declaration to a member function in C++ because extern "C" refers to the calling conventions of the C language which doesn't have classes and member functions. The most logical solution is to use the mangled function name.
The quote comes frome Agner Fog's
Optimizing subroutines in assembly language, section 7.4, p. 48 and 49. Agner continues:
Quote
The mangled function name ... is compiler specific. Other compilers may have other name-mangling codes. Furthermore, other compilers may put 'this' on the stack rather than in a register.
Bobl, you should read that section. Agner has code samples and good explanations for that what you would like to do. I've provided a link to Agner's manuals in my first post above.
Gunther
Gunther
I just came back to say that for the last 30 minutes all my attempts to get the address of the first string in the list
has resulted in a value of 1 which looks more like an index than an address. You comments look like an explanation.
I shall be investgating those.
Thank you very much for the links.
void cForth::Outer(TStringList * tkns){
char * p = tkns->Strings[0].c_str();
ShowMessage(p); //this is printing the string fine i.e. as if p is an address
ShowMessage(IntToStr(p)); //this is giving me 1 which doesn't look like an address
jj2007
That aside I don't think my asm stub was right so I'd appreciate an example of what you were expected for the asm stub and I'll try that.
My goodness...BCB projects are big compared to PB ones to the extent that it's well over 512Kb, as is,
and therefore unpostable.
I'm going to have to look in to this because it is useful to be able to post such stuff.
In fact I think I'll retire for the day, regroup, and have a good run at this tomorrow.
See you then and thank you for helping me to get this far.
Edit:
I don't like finishing off on a low note and getting 1 is precisely that....
I've been playing in delphi 3 and get a ptr's value doing this...
procedure TForm1.Button1Click(Sender: TObject);
var
myString : string;
ptrString : PString;
begin
// Set up variable values
myString := 'Hello there';
ptrString := Addr(myString);
ShowMessage(Format('Pointer = %p', [Addr(myString)]));
ShowMessage('myString : '+ptrString^);
end;
end.
I'll see whether this is transferrable to BCB tomorrow.
you might try to adapt the following lines:
typedef struct {
const char* data;
size_t count;
} raw_str;
//MASM:
// raw_str struct
// data PCHAR ?
// count DWORD ?
// raw_str ends
//...
// try {...
if(tkns->Count) {
raw_str* praw = new raw_str[tkns->Count];
for(int i=0; i < tkns->Count; i++) {
praw[i].data = tkns->Strings[i].c_str();
praw[i].count = tkns->Strings[i].Length();
}
// call MASM function
// e.g.: Cpp: extern "C" bool foo(raw_str* p,size_t n)
// MASM: foo PROC/PROTO C p: ptr raw_str,n:DWORD
//foo(praw,tkns->Count)
delete [] praw;
} //...
Hi qWord,
Quote from: qWord on April 28, 2013, 04:23:01 AM
you might try to adapt the following lines:
I've my doubts that this will work. The assembly language function isn't with extern "C" a class member (see reply #11 of this thread). One could try the access with a friend function, but that's not easy.
Gunther
Quote from: Gunther on April 28, 2013, 04:39:16 AMI've my doubts that this will work. The assembly language function isn't with extern "C" a class member (see reply #11 of this thread). One could try the access with a friend function, but that's not easy.
it should work (at least) as long as he does not put the function in any namespace/class.
Hi qWord,
Here is a quote from bobl's first post:
Quote
Re the strings. At present they're in a TStringList...which is a class.
That's the tricky point.
Gunther
Quote from: Gunther on April 28, 2013, 06:17:54 AMQuote
Re the strings. At present they're in a TStringList...which is a class.
That's the tricky point.
you are thinking to complicated: he can declare the function outside the class (=>global scope in corresponding module) and then call it inside a wrapper method (of class XYZ).
Hi qWord,
Quote from: qWord on April 28, 2013, 06:29:31 AM
you are thinking to complicated: he can declare the function outside the class (=>global scope in corresponding module) and then call it inside a wrapper method (of class XYZ).
did you ever try that?
Gunther
I guess passing an array from C++ to Assembler is not a big deal - Olly will reveal how to do that ;-)
In the meantime, I've tested the opposite direction: Let Masm create and handle the string array. Below are the assembler and C++ sources. In VC++ 2010 Express, it works if a) VC and Masm32 are on the same drive and b) after assembling, you drag the RecallArray.obj (attached below) over the bold
Recall in VC's solution explorer window.
Assembler, RecallArray.asm:Quoteinclude \masm32\MasmBasic\MasmBasic.inc ; Pure Masm (http://masm32.com/board/index.php?topic=94.0)TM
.data?
ctStrings dd ?
.code
AsmLoadArray proc C filename ; loads textfile to string array
Recall filename, My$()
mov ctStrings, eax
ret ; returns #strings loaded
AsmLoadArray endp
Asm$ proc C inx ; returns Asm$(index)
mov eax, My$(inx) ; returns ptr to string
ret
Asm$ endp
SetAsm$ proc C inx, newtext ; assigns new string to array element
Let My$(inx)=newtext ; no return value
ret
SetAsm$ endp
end
C++, Recall.cpp:Quote#include <stdio.h>
extern "C" int AsmLoadArray(char* filename);
extern "C" char* Asm$(int index);
extern "C" int SetAsm$(int index, char* newtext);
int main () {
int i, NumStrings=AsmLoadArray("C:\\Masm32\\Examples\\exampl01\\generic\\generic.asm");
printf("%i strings loaded\n", NumStrings);
SetAsm$(NumStrings-2, "\tWe played a little trick");
SetAsm$(NumStrings-1, Asm$(NumStrings-3));
for (i=0;i<20;i++){
printf("%i\t%s\n", i, Asm$(i));
}
printf("%s\n", "...");
for (i=NumStrings-20;i<NumStrings;i++){
printf("%i\t%s\n", i, Asm$(i));
}
void getc();
return 0;
}
The code itself is trivial. I spent half an evening, however, to convince VC that printing to a console window is an important activity (and was honoured by endless error messages), and the other half to find out that VC and Masm32 have to be on the same drive. Now I am collecting a long list of words that start with C, like crap, creeps, clumsy etc, in order to push my blood pressure down to normal :bgrin:
P.S. - Just one example why I consider VC a sh***y product:
- press in VC Ctrl F5 to build the Recall project (should work if the obj & lib from the archive are there)
- Rename MasmBasic.lib to whatever.lib
- press in VC F5:
1. You will see for some microseconds the usual output (why does that bloody M$ product not recognise that a console window must stay open??? especially since there is a getc...), proof that the exe works
2. Endless complaints à la 'Recall.exe': Loaded 'C:\WINDOWS\system32\ntdll.dll', Cannot find or open the PDB file
3. At the end, "The program '[3028] Recall.exe: Native' has exited with code 0 (0x0)."
Great, it worked :t
- now press in VC F7: === Build: 0 succeeded, 0 failed, 1 up-to-date, 0 skipped ===
Great, it worked :t
- now press in VC Ctrl F7:
------ Build started: Project: Recall, Configuration: Debug Win32 ------
Skipping... (no relevant changes detected)
Recall.cpp
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========
Great, it worked :t
- now press in VC Ctrl F5:
------ Build started: Project: Recall, Configuration: Debug Win32 ------
LINK : fatal error LNK1104:
cannot open file '\masm32\MasmBasic\MasmBasic.lib'
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
IMHO Bill Gates should be thrown in jail 8)
Quote from: Gunther on April 28, 2013, 09:25:13 AMdid you ever try that?
yes.
the following example works with VS2010 and g++ 4.6.2.
#include <iostream>
#include <list>
extern "C" bool __cdecl fn1(const char** p,int n);
class foo
{
std::list<std::string*> tkns;
public:
foo() {
tkns.push_back(new std::string("foo1"));
tkns.push_back(new std::string("foo2"));
tkns.push_back(new std::string("foo3"));
}
~foo() {
for(std::list<std::string*>::iterator itr = tkns.begin(); itr!=tkns.end();itr++)
delete *itr;
}
bool callMASM() {
try {
const char** ppsz = new const char*[tkns.size()];
int i=0;
for(std::list<std::string*>::iterator itr = tkns.begin(); itr!=tkns.end();itr++)
ppsz[i++] = (*itr)->c_str();
bool ret = fn1(ppsz,tkns.size());
delete [] ppsz;
return ret;
}
catch (...) {std::cout << "error\n"; return false;}
}
};
int main() {
foo x;
x.callMASM();
return 0;
}
include \masm32\include\masm32rt.inc
.code
fn1 proc C uses esi edi p:ptr PCHAR,n:DWORD
xor esi,esi
mov edi,p
.while esi < n
print PCHAR ptr [edi+esi*PCHAR],13,10
inc esi
.endw
mov eax,TRUE
ret
fn1 endp
end
I'm very grateful to you all for your experiments and persistence.
It helps a lot!
I'll post what I've done when I'm happy with it.
Thank you all very much indeed.
Hi bobl,
Quote from: bobl on April 28, 2013, 11:18:42 PM
I'm very grateful to you all for your experiments and persistence.
It helps a lot!
I'll post what I've done when I'm happy with it.
Thank you all very much indeed.
that's what friends are for. :icon_cool:
Gunther
That's very kind of you Gunther. Thank you.
I said I'd come back when I got something I was happy with.
I lied.
I've been trying to apply QWORD'S assembler function to TStringLists as simply as possible to start with...
//---------------------------------------------------------------------------
#include <vcl.h>
#include <iostream.h>
using namespace std;
#pragma hdrstop
//---------------------------------------------------------------------------
extern "C" bool __cdecl fn1(const char** p,int n);
#pragma argsused
int main(int argc, char* argv[])
{
int i,j;
TStringList * tkns = new TStringList;
tkns->Add("aaa");
tkns->Add("bbb");
tkns->Add("ccc");
char ** pp;
const char ** cpp;
char * p[3];
p[0] = tkns->Strings[0].c_str();
p[1] = tkns->Strings[1].c_str();
p[2] = tkns->Strings[2].c_str();
pp = p;
cpp = (const char **)pp;
//test =========================
cout << cpp[0] << "\n"; //tick
cout << cpp[1] << "\n"; //tick
cout << cpp[2] << "\n"; //tick
//end test =====================
fn1(cpp,2);
cin >> j;
delete tkns;
return 0;
}
Unfortunately...including the line...fn1(cpp,2);... is giving me the message...
[Linker Error] Error: Unresolved external '_StdOut@4' referenced from C:\MY_DELPHI\MY_CBUILDER2006\PROJ4\MYOMFDMASM.OBJ
Any ideas anyone?
I'm just wondering if there's something intrinsically wrong with my C++?
Quote from: bobl on April 29, 2013, 01:48:30 AMUnfortunately...including the line fn1() is giving me the message...
[Linker Error] Error: Unresolved external '_StdOut@4' referenced from C:\MY_DELPHI\MY_CBUILDER2006\PROJ4\MYOMFDMASM.OBJ
Any ideas?
add the library
\masm32\lib\masm32.lib
Thanks for the advice
Because it didn't satisfy BCB2006's OMF requirements I copied masm32.lib to the project directory and subjected it to Agner Fog's objconv as advised.
It's certainly moved things on because I'm now getting the messages shown.
I'm guessing they're other "missing" libraries that I need to objconv and copy (so as not to mess up the lib directory)
...I'll have a look.
Getting there!
[Linker Error] Error: Unresolved external '_GetStdHandle@4' referenced from C:\MY_DELPHI\MY_CBUILDER2006\PROJ4\MAS32_OMFD.LIB|stdout.obj
[Linker Error] Error: Unresolved external '_WriteFile@20' referenced from C:\MY_DELPHI\MY_CBUILDER2006\PROJ4\MAS32_OMFD.LIB|stdout.obj
It is strange that the kernel32.lib is not already involved in the link process - either use the kernel32.lib from the MASM32 SDK or the one that (probably) comes with your compiler/SDK/...
Oh Ok Thanks!
when I try to objconv kernel32.lib from masm32...using -fomf...I get
Input library: c:\my_delphi\my_cbuilder2006\proj4\kernel32.lib, Format: COFF, Ou
tput: omfd_kernel32.lib, Format: OMF
Error 2030: Unsupported relocation type (0)
Error 2030: Unsupported relocation type (0)
Error 2030: Unsupported relocation type (0)
C:\Documents and Settings\me\My Documents\Downloads\objconv>
Just a note to myself to say that although there's a kernel32.lib at "program files\Visual Studio...."
the one we're looking for now is here...C:\Program Files\Borland\BDS\4.0\lib\psdk
Edit:
I just added the lib using shift/F11 and I think these are the same errors
[Linker Error] Error: Unresolved external '_GetStdHandle@4' referenced from C:\MY_DELPHI\MY_CBUILDER2006\PROJ4\MAS32_OMFD.LIB|stdout.obj
[Linker Error] Error: Unresolved external '_WriteFile@20' referenced from C:\MY_DELPHI\MY_CBUILDER2006\PROJ4\MAS32_OMFD.LIB|stdout.obj
Just a thought...is there a c++ phrase (No of lines) I could try to exercise '_GetStdHandle@4' to rule out a faulty BDS2006 thing or my incompetetent installation of it(which it well may be)?
Edit:
I've just asked on Embacadero's forum if the above errors are known about.
It might be prudent to wait and see if this is some sort of known bug.
Quote from: bobl on April 29, 2013, 04:01:18 AMI've just asked on Embacadero's forum
a really funny forum: C++64 memcpy performance (https://forums.embarcadero.com/thread.jspa?threadID=82295&tstart=0) (read the 1. and 4. post) :biggrin:
Quotemov al,[rdx]
DednDave and jj2007 vote for rep movsb 8)
well - rep movsd isn't bad - lol
at least, not if both source and dest are 4-aligned
That's quite an eye opener re the 64bit stuff.
To add kernel32.lib I tried...
shift/F11,
adding it's directory to the path environment variable
and looking for a header file to include (there doesn't seem to be a kernel32.h)
but am still not seeing it on the linker line. I guessing I should.
I asked on Embarcadero's forum this morning how to do this but no-one's come back.
Does anyone here know?
I feel a bit embarrased asking.
Hi qWord,
Quote from: qWord on April 29, 2013, 09:39:36 AM
a really funny forum: C++64 memcpy performance (https://forums.embarcadero.com/thread.jspa?threadID=82295&tstart=0) (read the 1. and 4. post) :biggrin:
that's real fun. :t
Gunther
bobl,
you only need to add .....lib\PSDK\kerenl32.lib to the linker command line as you did is with the object file created by MASM.
(Because this lib comes with your compiler, there is no need to convert it).
Thanks for the advice. I thought I'd tried that but just double checked.
I added the masm obj/lib using shift/f11 and selecting the files.
I've just used shift/F11 with BCB's own kernel32.lib
Here's the linker line now...
Linker command line
-Tpe -Gn -v -L"c:\program files\borland\bds\4.0\lib\debug";C:\my_delphi\my_cbuilder2006\proj4;"c:\program files\borland\bds\4.0\lib";"c:\program
files\borland\bds\4.0\lib\obj";"c:\program files\borland\bds\4.0\lib\psdk";"c:\program files\borland\bds\4.0\Include\Indy9";"c:\program
files\borland\bds\4.0\Lib\Indy9";"C:\Documents and Settings\me\My Documents\Borland Studio Projects\bpl" -j -x -LC:\my_delphi\my_cbuilder2006\proj4
-GA"C:\my_delphi\my_cbuilder2006\proj4\vfs27.tmp"="C:\my_delphi\my_cbuilder2006\proj4\Project1.res" c0x32.obj vcl.bpi rtl.bpi vclx.bpi dbrtl.bpi
vcldb.bpi adortl.bpi dbxcds.bpi dbexpress.bpi vclib.bpi ibxpress.bpi xmlrtl.bpi vclactnband.bpi inet.bpi IntrawebDB_80_100.bpi Intraweb_80_100.bpi
vclie.bpi inetdbbde.bpi inetdbxpress.bpi indy.bpi bcbsmp.bpi soaprtl.bpi dsnap.bpi bcbie.bpi bdertl.bpi teeui.bpi teedb.bpi tee.bpi vcldbx.bpi
memmgr.lib sysinit.obj C:\my_delphi\my_cbuilder2006\proj4\Debug_Build\Unit1.obj
C:\my_delphi\my_cbuilder2006\proj4\myomfdmasm.obj,C:\my_delphi\my_cbuilder2006\proj4\Debug_Build\Project1.exe,C:\my_delphi\my_cbuilder2006\proj4\Debug_Build\Project1.map,import32.lib
C:\my_delphi\my_cbuilder2006\proj4\mas32_omfd.lib "C:\Program Files\Borland\BDS\4.0\lib\psdk\kernel32.lib" cp32mti.lib,,
C:\my_delphi\my_cbuilder2006\proj4\vfs27.tmp
[Linker Error] Error: Unresolved external '_GetStdHandle@4' referenced from C:\MY_DELPHI\MY_CBUILDER2006\PROJ4\MAS32_OMFD.LIB|stdout.obj
[Linker Error] Error: Unresolved external '_WriteFile@20' referenced from C:\MY_DELPHI\MY_CBUILDER2006\PROJ4\MAS32_OMFD.LIB|stdout.obj
The line
C:\my_delphi\my_cbuilder2006\proj4\mas32_omfd.lib "C:\Program Files\Borland\BDS\4.0\lib\psdk\kernel32.lib"
suggests that kernel32.lib is added
I also note that...."c:\program files\borland\bds\4.0\LIB\psdk" ie kernel32.lib's directory is there too higher up.
I'm therefore surprised that I'm still getting the error that you can see on the end of the linker command line
Check out the attached image
FWIW _GetStdHandle@4 is not in kernel32.lib oer se but there is a single reference to GetStdHandle enclosed by ^L symbols
Here's the bit of kernel32.lib that contains getstdhandle.
I have no idea whether it is what is expected or not.
BTW I'm not expecting the masm forum to be BCB experts and have posted this question on the compiler's own forum.
mpletionStatus.KERNEL32.dll.Έ%.. ...GetShortPathNameA.KERNEL32.dll.†ˆ%.. ...GetShortPathNameW.KERNEL32.dll.pˆ#.. ...GetStartupInfoA.KERNEL32.dll.)ˆ#.. ...GetStartupInfoW.KERNEL32.dll..ˆ .. ...GetStdHandle.KERNEL32.dll.xˆ".. ...GetStringTypeA.KERNEL32.dll.'ˆ$.. ...GetStringTypeExA.KERNEL32.dll.Ј$.. ...GetStringTypeExW.KERNEL32.dll.ºˆ".. ...GetStringTypeW.KERNEL32.dll.{ˆ(.. ...GetSystemDefaultLCID.KERNEL32.dll.yˆ
The plot thickens...
If i do
extern GetStdHandle;
extern WriteFile;
I get
C++ command line for "Unit1.cpp"
-D_DEBUG -DUSEPACKAGES;NO_STRICT;_RTLDLL -H="c:\program files\borland\bds\4.0\lib\vcl100.csm" -Hc -w-par -Od -b- -k -y -v -vi- -tWC -tWM -tW- -c
-IC:\my_delphi\my_cbuilder2006\proj4;"c:\program files\borland\bds\4.0\include";"c:\program files\borland\bds\4.0\include\dinkumware";"c:\program
files\borland\bds\4.0\include\vcl";"c:\program files\borland\bds\4.0\Include\Indy9";"c:\program files\borland\bds\4.0\Lib\Indy9"
-oC:\my_delphi\my_cbuilder2006\proj4\Debug_Build\Unit1.obj -Hr"vcl.h"="vcl.h"
[C++ Error] Unit1.cpp(11): E2356 Type mismatch in redeclaration of '__stdcall GetStdHandle(unsigned long)'
[C++ Error] winbase.h(3550): E2344 Earlier declaration of '__stdcall GetStdHandle(unsigned long)'
[C++ Error] Unit1.cpp(12): E2356 Type mismatch in redeclaration of '__stdcall WriteFile(void *,const void *,unsigned long,unsigned long *,_OVERLAPPED *)'
[C++ Error] winbase.h(3569): E2344 Earlier declaration of '__stdcall WriteFile(void *,const void *,unsigned long,unsigned long *,_OVERLAPPED *)'
If I extern the same symbols that the linker is complaining about
extern _GetStdHandle@4;
extern _WriteFile@20;
I get
C++ command line for "Unit1.cpp"
-D_DEBUG -DUSEPACKAGES;NO_STRICT;_RTLDLL -H="c:\program files\borland\bds\4.0\lib\vcl100.csm" -Hc -w-par -Od -b- -k -y -v -vi- -tWC -tWM -tW- -c
-IC:\my_delphi\my_cbuilder2006\proj4;"c:\program files\borland\bds\4.0\include";"c:\program files\borland\bds\4.0\include\dinkumware";"c:\program
files\borland\bds\4.0\include\vcl";"c:\program files\borland\bds\4.0\Include\Indy9";"c:\program files\borland\bds\4.0\Lib\Indy9"
-oC:\my_delphi\my_cbuilder2006\proj4\Debug_Build\Unit1.obj -Hr"vcl.h"="vcl.h"
[C++ Error] Unit1.cpp(11): E2206 Illegal character '@' (0x40)
[C++ Error] Unit1.cpp(11): E2141 Declaration syntax error
[C++ Error] Unit1.cpp(12): E2206 Illegal character '@' (0x40)
[C++ Error] Unit1.cpp(12): E2141 Declaration syntax error
This looks similar to what I'm encountering
http://stackoverflow.com/questions/14726346/how-do-i-link-with-freepascal-a-nasm-program-calling-a-dll
I believe it's a linker requirement. it's delphi product right? usually they go with the OMF file format for the linker , if I'm not mistaken.
I found this thread : http://forum.dlang.org/thread/j0u16n$1g7t$1@digitalmars.com
for digital mars though, but it seem that the problem is the same.
so give this: http://ftp.digitalmars.com/coffimplib.zip a try..
good luck..
Dubby
I just assumed BCB's lib was OMF but maybe not...it never occurred to me.
Here's my attempt...
C:\Program Files\Borland\BDS\4.0\lib\psdk>coff_import_lib_to_omf_import_lib kern
el32.lib kernel32_omf.lib -f -l -v
coff2def 0.01
Error: missing archive signature
I don't know what "missing archive signature means" but it was well worth a try.
Thanks for the suggestion.
It does beg the question though how do you know whether somethings in OMF format or not unless you convert it.
I'll give it the objconv treatment...and see if that works
Edit:
It didn't...I get the same errors.
Thinking about it...this isn't surprising because up until now...every time there's been an OMF-incompatibility the compiler/linker has signalled it and it's always been solved with objconv. There's no such signal with kernel32.lib.
An embarcadero forum member seems to think that my error messages show more decoration than there should be and that this might be a cdecl vs std_call incompatibility...I don't know...Any suggestion re the best way to test this?
I'm quite happy to follow this up but don't know how.
wait wait, wait,, just to make everything clear..
1. the unresolved come from masm assembled files right? what is the file? is it obj file or a lib file?
2. is it OK if you write the function which uses those, which caused unresolved error, with the C++ compiler ? in this case is BCB2006. right?
3. from where the kernel32.lib you link came from..?
as I never encountered with BCB, I can't really sure what is the problem like. my last suggestion is, if the result from question number 2 is OK, then you can open the obj or other similar file that the compiler produce with any hex editor and search the string which caused the previous error.. and see how's they looks..? I mean the decorative name or symbol or something like that..
Quote from: bobl on April 30, 2013, 03:48:23 PM
An embarcadero forum member seems to think that my error messages show more decoration than there should be and that this might be a cdecl vs std_call incompatibility...I don't know...Any suggestion re the best way to test this?
I guess this is a correct assumption - the Borland import libraries contain
undecorated publics ( missing the '_'-prefix and the '@nn'-suffix ).
Consequently, any coff2omf utilities won't fix this issue.
There are a few possibilities how a solution might be achieved:
1. create an OMF import library that contains decorations ( OW wlib or jwlib can do this )
2. adjust the source that contains the decorated imports and make it use undecorated variants
3. use another linker that can both handle OMF and COFF import libraries
All options are a bit "advanced".
Dubby
I'm very grateful for your thoughts and agree that narrowing down the source of the problem by elimination
i.e. testing that this is not an inherent problem in BCB2006 itself is a good idea.
My problem is...my knowledge at this level is all but non-existent and I wouldn't know what to use in c++ to exercise these symbols. I do need to address this weakness and am only going to do this through exposure so perhaps this problem is no bad thing.
Japeth
>a bit "advanced"
You're not joking but I suppose I'm asking for trouble doing what I'm trying to do with my limited understanding so it's sort of self-inflicted. Of the three options I'd prefer...the JWLink one
i.e. a while back it succeeded (in MichaelW's hands) on some gcc stuff where gcc's own failed
and since then I've always thought that when I get around to "doing" linkers...JWLink would be the one.
Perhaps it's about time now.
Having said that to solve this sort of problem I probably need to at least understand what all 3 options really do.
I'll have a read around.
Thank you very much indeed for the pointers.
Quote from: bobl on April 30, 2013, 07:27:27 PM
You're not joking but I suppose I'm asking for trouble doing what I'm trying to do with my limited understanding so it's sort of self-inflicted. Of the three options I'd prefer...the JWLink one
i.e. a while back it succeeded (in MichaelW's hands) on some gcc stuff where gcc's own failed
and since then I've always thought that when I get around to "doing" linkers...JWLink would be the one.
Perhaps it's about time now.
Please note that I did sort my suggestions, by ( assumed ) ascending level of difficulty.
jwlink has been placed as the last because I - vaguely - remember that Borland may use some proprietary interpretations of OMF records for its C++ compiler - jwlink might be unaware of such "extensions".
Hence my suggestion is: try option 1 first.
Will do and thank you for the direction
In searching the net to understand how compilation works I accidentally came across this
http://flap.mynetmemo.com/?p=19
which might make things easier...Borland Developer Studio 2006 doesn't sem to have implib (perhaps I didn't install it) but luckily C++ Builder 4 does.
I thought you used either a static link library OR a dll..I'm therefore seeking clarity re the command line and fact that both are in it at the same time i.e. is that essential if you only intend to use a lib on it's own or are you just creating a universal import library that serves both?
To build a project that uses the DLL you will need to generate a Borland
import library. BCB6 comes with a command line tool called implib that will
do this. Simply open a command promt window, change to the directory where
the DLL is located, and run the following command (assuming that the Borland
bin directory is on your path);
implib -a sqlite.lib sqlite.dll
The -a argument is required to generate the correct symbol names in the
import library.
Any advice appreciated.
Edit:
Oops...Is this the answer to my question. If yes what's a static link library i.e. is it an import library by another name?
For the former, an import library (.lib file) will be required
Hi bobl,
Digital Mars is providing a tool to create OMF import libraries, implib.exe :
http://www.digitalmars.com/ctg/implib.html
You can visit this page to download the basic utilities package. It contains implib.exe
http://www.digitalmars.com/download/freecompiler.html
An example usage :
implib.exe kernel32.lib kernel32.def
Gosh Vortex that was a rapid response.
I've just re-read what Japeth said
>the Borland import libraries contain undecorated publics ( missing the '_'-prefix and the '@nn'-suffix ).
>1. create an OMF import library that contains decorations ( OW wlib or jwlib can do this )
Given this I suppose Borland's implib probably wouldn't decorate the import library so my reasoning seems false.
Thanks for the link...I'll check it out.
I also have JWLink to play with which seems to invoke the JWLib library manager to create import libraries...
with the right command line.
Vortex
Just came back to say...your first link offers a very good explanation of the process...Thank you very much.
It was very easy to create the import library for kernel32.lib with implib using kernel32.defs (there was no dll)
and I was hopeful that it would work because it was about 3X the size of the Borland one but...
unfortunately I'm still getting what I think are the same errors i.e.
Linker command line
-Tpe -Gn -v -L"c:\program files\borland\bds\4.0\lib\debug";C:\my_delphi\my_cbuilder2006\proj4;"c:\program files\borland\bds\4.0\lib";"c:\program
files\borland\bds\4.0\lib\obj";"c:\program files\borland\bds\4.0\lib\psdk";"c:\program files\borland\bds\4.0\Include\Indy9";"c:\program
files\borland\bds\4.0\Lib\Indy9";"C:\Documents and Settings\me\My Documents\Borland Studio Projects\bpl" -j -x -LC:\my_delphi\my_cbuilder2006\proj4
-GA"C:\my_delphi\my_cbuilder2006\proj4\vfs3C.tmp"="C:\my_delphi\my_cbuilder2006\proj4\Project1.res" c0x32.obj vcl.bpi rtl.bpi vclx.bpi dbrtl.bpi
vcldb.bpi adortl.bpi dbxcds.bpi dbexpress.bpi vclib.bpi ibxpress.bpi xmlrtl.bpi vclactnband.bpi inet.bpi IntrawebDB_80_100.bpi Intraweb_80_100.bpi
vclie.bpi inetdbbde.bpi inetdbxpress.bpi indy.bpi bcbsmp.bpi soaprtl.bpi dsnap.bpi bcbie.bpi bdertl.bpi teeui.bpi teedb.bpi tee.bpi vcldbx.bpi
memmgr.lib sysinit.obj C:\my_delphi\my_cbuilder2006\proj4\Debug_Build\Unit1.obj
C:\my_delphi\my_cbuilder2006\proj4\myomfdmasm.obj,C:\my_delphi\my_cbuilder2006\proj4\Debug_Build\Project1.exe,C:\my_delphi\my_cbuilder2006\proj4\Debug_Build\Project1.map,import32.lib
C:\my_delphi\my_cbuilder2006\proj4\mas32_omfd.lib "C:\Program Files\Borland\BDS\4.0\lib\psdk\kernel32.lib" cp32mti.lib,,
C:\my_delphi\my_cbuilder2006\proj4\vfs3C.tmp
[Linker Error] Error: Unresolved external '_GetStdHandle@4' referenced from C:\MY_DELPHI\MY_CBUILDER2006\PROJ4\MAS32_OMFD.LIB|stdout.obj
[Linker Error] Error: Unresolved external '_WriteFile@20' referenced from C:\MY_DELPHI\MY_CBUILDER2006\PROJ4\MAS32_OMFD.LIB|stdout.obj
Perhaps the def file isn't decorated and implib just uses what it's got without decoration
Hi bobl,
You can download the attachment containing some decorared module definition files : kernel32,user32,gdi32,shell32,comctl32,comdlg32,advapi32 and msvcrt.def
Creating those def files with lib2def is easy :
QuoteMS COFF import library to module definition file converter V2.0
lib2def converts MS COFF import libraries to module definition files. lib2def accepts wildcards like *.lib
http://vortex.masmcode.com/files/lib2def21.zip
EDIT : You can remove the double quotes in the def files if implib complains about them.
Vortex
I've downloaded those and will try them when I get back tonight.
Thank you for your kindness in providing them.
It's very much appreciated!
Well things have certainly moved on...
I created the import library i.e. kernel32.lib...
using Vortex's decorated kernel32.def in conjunction with the Digital Mars implib.exe command line
"implib /s kernel32.lib kernel32.dll"
I now get a message box entitled...
"Project1.exe...Entry point not found"
with the message...
"The procedure entry point _GetStdHandle@4 could not be located in the dynamic link library KERNEL32."
That looks like progress but I'm not sure what to do in response to the message.
Also I have 3 such DLLs ie. in...
c:\WINDOWS\system32
c:\WINDOWS\ServicePackFiles\i386 &
C:\WINDOWS\$NtServicePackUninstall$
BTW I did try to create the import library using the DLL in \system32 but that just gave me the old errors
i.e. only Vortex's decorated def file got me this far.
Any advice much appreciated.
Hi bobl,
implib /s kernel32.lib kernel32.dll
This looks incorrect.
implib kernel32.lib kernel32.def
The symbols in the def files have already a leading uderscore so need of the option /s
I did a test of my own, which I will describe in detail below:
1. I just installed the C++-builder command line tools - this is rather old stuff, AFAICS, they're installed by running freecommandlinetools.exe and the result is a directory C:\bcc55 with various sub-directories that contain the compiler, linker ( ilink32.exe ),, an implib.exe tool and finally include files and libraries ( import32.lib contains the undecorated Win32 imports, but isn't needed here ).
2. I wrote a small assembly test file (test.asm), assembled with masm/jwasm, output format OMF:
.386
.Model flat, stdcall
option casemap:none
GetStdHandle proto :dword
WriteFile proto :dword, :dword, :dword, :dword, :dword
ExitProcess proto :dword
.code
szHello db "abc",13,10
main proc c
local dwWritten:dword
invoke GetStdHandle, -11
mov ebx, eax
invoke WriteFile, ebx, addr szHello, sizeof szHello, addr dwWritten, 0
invoke ExitProcess, 0
main endp
end main
3. I created an import library with this kernel32.DEF file:
library kernel32
exports
_ExitProcess@4
_GetStdHandle@4
_WriteFile@20
to create it, I run:
implib kernel32.lib kernel32.def
4. finally linked the object module with:
ilink32 test.obj kernel32.lib
and the resulting binary test.exe runs fine.
It looks like that Borland's linker cannot interpret corectly the import libraries created by Digital Mars' implib.exe
Vortex, Japeth
I'm very grateful for your advice and help and will try to do your suggestions justice.
Vortex...I'll try your decorated def file with the implib that Japeth has suggested and if that doesn't work with the implib that's in BCB4 that I have.
Thanks for bearing with me
SUCCCCCCCCCESS! with the first combination
i.e. Vortex's decorated kernel32.def + Japeth's recommendation re implib.exe and associated command line.
I REALLY appreciate your help in cracking this. Thank you!
>It looks like that Borland's linker cannot interpret corectly the import libraries created by Digital Mars' implib.exe
spot on!