I have compiled SQLIte3 database engine from sqlite3.c with BCC 55 using the following command:
bcc32.exe -jb -O2 -w- -K -c -6 -u- sqlite3.c
The proper sqlite3.obj file was generated. But once I try to link it in my Delphi application like this:
unit unt_SQLite3;
interface
uses
Windows;
implementation
{$LINK 'sqlite3.obj'}
end.
I get the following errors:
[DCC Error] E2065 Unsatisfied forward or external declaration: '__ftol'
[DCC Error] E2065 Unsatisfied forward or external declaration: '__lldiv'
[DCC Error] E2065 Unsatisfied forward or external declaration: '__llmod'
[DCC Error] E2065 Unsatisfied forward or external declaration: '_localtime'
[DCC Error] E2065 Unsatisfied forward or external declaration: '_strncmp'
[DCC Error] E2065 Unsatisfied forward or external declaration: '_memset'
[DCC Error] E2065 Unsatisfied forward or external declaration: '__llmul'
[DCC Error] E2065 Unsatisfied forward or external declaration: '_malloc'
[DCC Error] E2065 Unsatisfied forward or external declaration: '_free'
[DCC Error] E2065 Unsatisfied forward or external declaration: '_realloc'
[DCC Error] E2065 Unsatisfied forward or external declaration: '_memcpy'
[DCC Error] E2065 Unsatisfied forward or external declaration: '__llumod'
[DCC Error] E2065 Unsatisfied forward or external declaration: '__lludiv'
[DCC Error] E2065 Unsatisfied forward or external declaration: '_memmove'
[DCC Error] E2065 Unsatisfied forward or external declaration: '_memcmp'
[DCC Error] E2065 Unsatisfied forward or external declaration: '__llshl'
[DCC Error] E2065 Unsatisfied forward or external declaration: '__llshr'
[DCC Error] E2065 Unsatisfied forward or external declaration: '_atol'
[DCC Error] E2065 Unsatisfied forward or external declaration: '_strlen'
[DCC Error] E2065 Unsatisfied forward or external declaration: '_qsort'
[DCC Error] E2065 Unsatisfied forward or external declaration: '__llushr'
[DCC Error] E2065 Unsatisfied forward or external declaration: '__turboFloat'
Why is needed to implement the Borland C++ runtime functions in pure pascal(or asm)? Can't those linked in the obj directly? Some of them are already implemented in System.pas but yet the compiler complains?
The rational behind doing this mysqlf instead of using SynSQLite3 or DIXml is the following:
SynSQLite3 supports 3.7.8 (I do not see the latest 3.7.9)
SynSQLite3 misses some declarations like sqlite3_trace, sqlite_open_v2, etc.
SynSQLite2 is around 18 times slower than DIXml 2.4.0 in consequent 20 000 step operations
DISQLite3 is paid
DISQLite 2.4.0 is fast does 20000 step operations in 260ms but does not support DXE2
DISQLite 3.0.0 and 3.1.0 do support DXE2 but are around 8 times slower than 2.4.0
I am very curious guy and always try to code as close to the metal as possible.
Kudos to SynSQLite3 and DISQLite3 developers - really good work doen so far
Eventually I ended up choosing SynSQLite3 because:
It is open source
It is very well documented
I learned how to recompile sqlite3.obj myself and leave just the needed compilation switches for the features I need
I can have the updated 3.7.9 version linked
With the fine tuned latest 3.7.9 obj I achieved the speed of DISQLite3
The DISQLite3 guy does not have even an email address on his site to write to (just a mailing list), where the SynSQLite3 guys reply in SO on the same hour. This makes sense when choosing one lib over another. Performance and price is not everything.
P.S. My sqlite3.obj is temporaly available for download and test here
Take a look at our Open Source libraries. It implements static linking of sqlite3.obj, and is maintained with the latest version of SQLite3 official code (and features - it is the only framework allowing extended use of the SQLite3 Virtual Tables, for instance). You've got a wrapper. But more than that.
Here is how we compile the source into the .obj (one inclusing FTS3, the other without it):
del sqlite3.obj
\dev\bcc\bin\bcc32 -6 -O2 -c -d -DSQLITE_ENABLE_FTS3 -u- sqlite3.c
copy sqlite3.obj sqlite3fts3.obj
\dev\bcc\bin\bcc32 -6 -O2 -c -d -u- sqlite3.c
Then take a look at the SynSQLite3.pas unit. It contains some pure pascal or asm version of the needed external files.
For instance:
function _ftol: Int64;
// Borland C++ float to integer (Int64) conversion
asm
jmp System.@Trunc // FST(0) -> EDX:EAX, as expected by BCC32 compiler
end;
function _ftoul: Int64;
// Borland C++ float to integer (Int64) conversion
asm
jmp System.@Trunc // FST(0) -> EDX:EAX, as expected by BCC32 compiler
end;
function malloc(size: cardinal): Pointer; cdecl; { always cdecl }
// the SQLite3 database engine will use the FastMM4/SynScaleMM fast heap manager
begin
GetMem(Result, size);
end;
procedure free(P: Pointer); cdecl; { always cdecl }
// the SQLite3 database engine will use the FastMM4 very fast heap manager
begin
FreeMem(P);
end;
function realloc(P: Pointer; Size: Integer): Pointer; cdecl; { always cdecl }
// the SQLite3 database engine will use the FastMM4/SynScaleMM very fast heap manager
begin
result := P;
ReallocMem(result,Size);
end;
function memset(P: Pointer; B: Integer; count: Integer): pointer; cdecl; { always cdecl }
// a fast full pascal version of the standard C library function
begin
result := P;
FillChar(P^, count, B);
end;
procedure memmove(dest, source: pointer; count: Integer); cdecl; { always cdecl }
// a fast full pascal version of the standard C library function
begin
Move(source^, dest^, count); // move() is overlapping-friendly
end;
procedure memcpy(dest, source: Pointer; count: Integer); cdecl; { always cdecl }
// a fast full pascal version of the standard C library function
begin
Move(source^, dest^, count);
end;
function atol(P: pointer): integer; cdecl; { always cdecl }
// a fast full pascal version of the standard C library function
begin
result := GetInteger(P);
end;
var __turbofloat: word; { not used, but must be present for linking }
// Borland C++ and Delphi share the same low level Int64 _ll*() functions:
procedure _lldiv;
asm
jmp System.@_lldiv
end;
procedure _lludiv;
asm
jmp System.@_lludiv
end;
procedure _llmod;
asm
jmp System.@_llmod
end;
procedure _llmul;
asm
jmp System.@_llmul
end;
procedure _llumod;
asm
jmp System.@_llumod
end;
procedure _llshl;
asm
jmp System.@_llshl
end;
procedure _llshr;
asm
{$ifndef ENHANCEDRTL} // need this code for Borland/CodeGear default System.pas
shrd eax, edx, cl
sar edx, cl
cmp cl, 32
jl @@Done
cmp cl, 64
jge @@RetSign
mov eax, edx
sar edx, 31
ret
@@RetSign:
sar edx, 31
mov eax, edx
@@Done:
{$else}
// our customized System.pas didn't forget to put _llshr in its interface :)
jmp System.@_llshr
{$endif}
end;
procedure _llushr;
asm
jmp System.@_llushr
end;
function strlen(p: PAnsiChar): integer; cdecl; { always cdecl }
// a fast full pascal version of the standard C library function
begin // called only by some obscure FTS3 functions (normal code use dedicated functions)
result := SynCommons.StrLen(pointer(p));
end;
function memcmp(p1, p2: pByte; Size: integer): integer; cdecl; { always cdecl }
// a fast full pascal version of the standard C library function
begin
if (p1<>p2) and (Size<>0) then
if p1<>nil then
if p2<>nil then begin
repeat
if p1^<>p2^ then begin
result := p1^-p2^;
exit;
end;
dec(Size);
inc(p1);
inc(p2);
until Size=0;
result := 0;
end else
result := 1 else
result := -1 else
result := 0;
end;
function strncmp(p1, p2: PByte; Size: integer): integer; cdecl; { always cdecl }
// a fast full pascal version of the standard C library function
var i: integer;
begin
for i := 1 to Size do begin
result := p1^-p2^;
if (result<>0) or (p1^=0) then
exit;
inc(p1);
inc(p2);
end;
result := 0;
end;
function localtime(t: PCardinal): pointer; cdecl; { always cdecl }
// a fast full pascal version of the standard C library function
var uTm: TFileTime;
lTm: TFileTime;
S: TSystemTime;
begin
Int64(uTm) := (Int64(t^) + 11644473600)*10000000; // unix time to dos file time
FileTimeToLocalFileTime(uTM,lTM);
FileTimeToSystemTime(lTM,S);
with atm do begin
tm_sec := S.wSecond;
tm_min := S.wMinute;
tm_hour := S.wHour;
tm_mday := S.wDay;
tm_mon := S.wMonth-1;
tm_year := S.wYear-1900;
tm_wday := S.wDayOfWeek;
end;
result := @atm;
end;
You'll find in this unit much more than just a static linking of SQLite3. Note that even if it is used by our mORMot ORM Client-Server framework, this ORM is not required to use the SQLite3 classes. See this article for additional details.
If you are lost into our source code repository (using the great FOSSIL project), read this.
Update: You will be far better off with SynSQLite3.pas
as suggested by Arnaud. However, I am leaving this answer here since it illustrates some of the tricks that can be used to resolve missing dependencies when static linking.
What is happening here is that the .obj file depends on various C runtime functions which need to be provided by you.
The first thing to do is to add crtl
to the uses
clause of the unit which contains the $LINK
directive. The crtl
unit contains implementations of a number of the C runtime library functions and is designed for just this purpose.
However, when you do this, whilst some missing dependencies are resolved, lots more appear.
Unsatisfied forward or external declaration: '_lldiv'
Unsatisfied forward or external declaration: '_llmod'
Unsatisfied forward or external declaration: 'localtime'
Unsatisfied forward or external declaration: '_llmul'
Unsatisfied forward or external declaration: 'InterlockedCompareExchange'
Unsatisfied forward or external declaration: 'InitializeCriticalSection'
Unsatisfied forward or external declaration: 'Sleep'
Unsatisfied forward or external declaration: 'DeleteCriticalSection'
Unsatisfied forward or external declaration: 'EnterCriticalSection'
Unsatisfied forward or external declaration: 'LeaveCriticalSection'
Unsatisfied forward or external declaration: '_llumod'
Unsatisfied forward or external declaration: '_lludiv'
Unsatisfied forward or external declaration: 'GetVersionExA'
Unsatisfied forward or external declaration: 'MultiByteToWideChar'
Unsatisfied forward or external declaration: 'WideCharToMultiByte'
Unsatisfied forward or external declaration: 'AreFileApisANSI'
Unsatisfied forward or external declaration: 'FormatMessageW'
Unsatisfied forward or external declaration: 'LocalFree'
Unsatisfied forward or external declaration: 'FormatMessageA'
Unsatisfied forward or external declaration: 'SetFilePointer'
Unsatisfied forward or external declaration: 'CloseHandle'
Unsatisfied forward or external declaration: 'ReadFile'
Unsatisfied forward or external declaration: 'WriteFile'
Unsatisfied forward or external declaration: 'SetEndOfFile'
Unsatisfied forward or external declaration: 'FlushFileBuffers'
Unsatisfied forward or external declaration: 'GetFileSize'
Unsatisfied forward or external declaration: 'LockFileEx'
Unsatisfied forward or external declaration: 'LockFile'
Unsatisfied forward or external declaration: 'UnlockFile'
Unsatisfied forward or external declaration: 'UnlockFileEx'
Unsatisfied forward or external declaration: 'UnmapViewOfFile'
Unsatisfied forward or external declaration: 'CreateFileMappingA'
Unsatisfied forward or external declaration: 'MapViewOfFile'
Unsatisfied forward or external declaration: 'GetTempPathW'
Unsatisfied forward or external declaration: 'GetTempPathA'
Unsatisfied forward or external declaration: 'CreateFileW'
Unsatisfied forward or external declaration: 'CreateFileA'
Unsatisfied forward or external declaration: 'GetFileAttributesW'
Unsatisfied forward or external declaration: 'DeleteFileW'
Unsatisfied forward or external declaration: 'GetFileAttributesA'
Unsatisfied forward or external declaration: 'DeleteFileA'
Unsatisfied forward or external declaration: 'GetFileAttributesExW'
Unsatisfied forward or external declaration: 'GetFullPathNameW'
Unsatisfied forward or external declaration: 'GetFullPathNameA'
Unsatisfied forward or external declaration: 'GetDiskFreeSpaceW'
Unsatisfied forward or external declaration: 'GetDiskFreeSpaceA'
Unsatisfied forward or external declaration: 'LoadLibraryW'
Unsatisfied forward or external declaration: 'LoadLibraryA'
Unsatisfied forward or external declaration: 'GetProcAddress'
Unsatisfied forward or external declaration: 'FreeLibrary'
Unsatisfied forward or external declaration: 'GetSystemTime'
Unsatisfied forward or external declaration: 'GetCurrentProcessId'
Unsatisfied forward or external declaration: 'GetTickCount'
Unsatisfied forward or external declaration: 'QueryPerformanceCounter'
Unsatisfied forward or external declaration: 'GetSystemTimeAsFileTime'
Unsatisfied forward or external declaration: 'GetSystemInfo'
Unsatisfied forward or external declaration: '_llshl'
Unsatisfied forward or external declaration: '_llushr'
Many of these are simply Windows API functions and can easily be resolved by adding Windows
to your uses clause.
At this point you are left with the following:
Unsatisfied forward or external declaration: '_lldiv'
Unsatisfied forward or external declaration: '_llmod'
Unsatisfied forward or external declaration: 'localtime'
Unsatisfied forward or external declaration: '_llmul'
Unsatisfied forward or external declaration: '_llumod'
Unsatisfied forward or external declaration: '_lludiv'
Unsatisfied forward or external declaration: '_llshl'
Unsatisfied forward or external declaration: '_llushr'
To resolve these you need to either:
$LINK
.I'm not actually sure what these functions do so you've got a bit more work ahead of you. My guess is that these functions are 64 bit integer arithmetic routines. You can probably reverse engineer this by writing short bits of C to perform various 64 bit arithmetic operations. Then compile with bcc32 and look at the output as asm
. Presumably bcc32
has the capability to emit asm
. Or your could just link to a Delphi unit and see which of the above functions corresponds to the operations you used in your C code.
You could pull localtime
out of msvcrt.dll
, always a useful fall-back option for missing C runtime functions. In fact, that's what the current implementation of the crtl
unit does, so if you are going to use crtl
you may as well get localtime
the same way.
Borrowing some code from Arnaud, the following unit compiles successfully:
unit sqlite3;
interface
implementation
uses
crtl, Windows;
{$L c:\desktop\sqlite3.obj}
procedure _lldiv;
asm
jmp System.@_lldiv
end;
procedure _llmod;
asm
jmp System.@_llmod
end;
procedure _llmul;
asm
jmp System.@_llmul
end;
procedure _llumod;
asm
jmp System.@_llumod
end;
procedure _lludiv;
asm
jmp System.@_lludiv
end;
procedure _llshl;
asm
jmp System.@_llshl
end;
procedure _llushr;
asm
jmp System.@_llushr
end;
procedure localtime; cdecl; external 'msvcrt.dll';
end.
Note that you don't need to provide parameter list, calling convention etc. for any of these functions since we are not implementing them here. In each case the code simply delegates the implementation.
However, this is still missing the code to declare the sqlite3 functions. What's more, I've not even attempted to test whether or not it works. Successful compilation is only the first step.
I strongly recommend that you use the code that Arnaud directs you towards if you wish to use static linking. This code clearly has had much use and testing and you may as well benefit from that.
Static linking makes for convenient deployment, but dynamic linking against a DLL is much simpler to achieve.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With