Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linking sqlite3.obj emits uunsatisfied forward declarations errors

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:

  1. It is open source

  2. It is very well documented

  3. I learned how to recompile sqlite3.obj myself and leave just the needed compilation switches for the features I need

  4. I can have the updated 3.7.9 version linked

  5. With the fine tuned latest 3.7.9 obj I achieved the speed of DISQLite3

  6. 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.

SQLite3 Profile Results

P.S. My sqlite3.obj is temporaly available for download and test here

like image 806
Gad D Lord Avatar asked Jan 01 '12 18:01

Gad D Lord


2 Answers

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.

like image 115
Arnaud Bouchez Avatar answered Nov 17 '22 19:11

Arnaud Bouchez


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:

  1. Link another .obj file containing the missing dependency.
  2. Implement the missing dependency in Delphi code in the same unit that contains the $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.

like image 41
David Heffernan Avatar answered Nov 17 '22 20:11

David Heffernan