News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Sqlite Error

Started by Custos, June 12, 2011, 12:59:15 AM

Previous topic - Next topic

Custos

Hello all.

I've been experimenting a little with Sqlite and have ran into a little snag.  I made a .lib and .inc file for the newest version using some tools.  I think it came out OK except for one little spot that deals with proto_sqlite3_destructor_type.
Maybe someone else can have a look and fix that.  The includes are in the attached file so feel free to have a look.

My problem is that I can create a database, create a table, and insert entries just fine but when I use a SELECT SQL command to get the entries it crashes after displaying 3 or 4 entries.  I have tried this with the includes I made and a dll and includes that someone else posted here (at least I think it was here) a while back (it came with some sample dictionary program).  With the newer includes and dll it displays the first 2 entries then displays the third entry incorrectly as the actual column name then crashes.  With the older dll and includes it displays four of the entries correctly then crashes without displaying the fifth one.

Anyway, attached is the sample program that illustrates the problem with sample code.  I hope someone can shed some light on this (Donkey still around?) as I know some of you have probably worked with sqlite.  Again for the gurus out there: go ahead and look at the includes I made to see if they are OK and feel free to use them. Note: I didn't attach the new dll but you can find it on the Sqlite page.

farrier

Custos,

You seem to be using invoke for the sqlite3_exec calls, without correcting the stack after the call!

The sqlite calls are not stdcall , they are cdecl and you must correct the stack for all the parameters you pass to the call.  sqlite3_exec passes 5 parameters, therefore you must correct the stack by 5 * 4 = 20 after each call.  You can see this if you run your exe thru OllyDbg and examine the stack pointer before and after each call to sqlite functions.

http://en.wikipedia.org/wiki/X86_calling_conventions

This may explain why the routine runs a few times and then explodes!!

hth,

farrier
It is a GOOD day to code!
Some assembly required!
ASM me!
With every mistake, we must surely be learning. (George...Bush)

farrier

Sorry, I didn't notice that the sqlite functions were defined as 'C' in the include file.

farrier
It is a GOOD day to code!
Some assembly required!
ASM me!
With every mistake, we must surely be learning. (George...Bush)

jj2007

Quote from: farrier on June 12, 2011, 01:49:11 PM
You seem to be using invoke for the sqlite3_exec calls, without correcting the stack after the call!

The sqlite calls are not stdcall , they are cdecl and you must correct the stack for all the parameters you pass to the call.

If the include file defines it as C, then invoke will take care of that.

Custos

Thanks for the replies guys!

What is funny about your thought is that what causes it to crash actually seems to be a stack related problem.  What happens is there is a place on the stack that gets overwritten with an invalid value.  Instead of being a valid memory location it gets replaced with the location of a place inside the dll.  It then tries to copy data there which is obviously a no no and thats where it crashes because of the access violation.  You should be able to see the location for yourself if you open in ollydbg and run it.  Also on the third run it incorrectly returns the name of the column and not the actual value of the column.  Maybe the stack is getting bumped each run somehow and thats what causes the third run to return incorrectly and then the fourth run to produce an access violation...

I can usually find what the problem is but this time the problem happens inside the sqlite dll and I don't know what would cause that.  It doesn't throw any errors itself it just gets caught in that access violation (and incorrect return value for run 3).
I suppose I could try the longer route of the core functions with sqlite prepare and all that but I really like the convenience of this wrapper function. :)

qWord

Quote from: Custos on June 12, 2011, 08:27:03 PMI can usually find what the problem is but this time the problem happens inside the sqlite dll and I don't know what would cause that.
as said before, the sqlite DLL works with C-calling convention: Your are passing a pointer to the callback function showentriescallback - this function is declared as stdcall, but cdecl is required.
Quoteshowentriescallback proc C lvrow:DWORD, numcol:DWORD, colvalarray:DWORD, colnamearray:DWORD
FPU in a trice: SmplMath
It's that simple!

jj2007

qWord is right, it works just fine with these little changes. But keep in mind that invoke takes care of the stack correction - do not correct it manually!

showentriescallback proto C :DWORD, :DWORD, :DWORD, :DWORD
...
invoke sqlite3_exec, hDatabase, addr szselect, addr showentriescallback, NULL, addr errormsg
...
showentriescallback proc C lvrow:DWORD, numcol:DWORD, colvalarray:DWORD, colnamearray:DWORD


0040122A            .  8D45 FC                 lea eax, [ebp-4]
0040122D            .  50                      push eax
0040122E            .  6A 00                   push 0
00401230            .  68 6D124000             push 0040126D                ; Entry point
00401235            .  68 60214000             push offset 00402160         ; ASCII "SELECT field FROM New;"
0040123A            .  FF35 30234000           push dword ptr [402330]
00401240            .  E8 BF000000             call <jmp.&SQLITE3.sqlite3_e ; Jump to SQLITE3.sqlite3_exec
00401245            .  83C4 14                 add esp, 14  <<< stack correction made by INVOKE !!!! ##

Custos

A simple but elegant solution!  Many thanks for that!

Speaking of the stack... it seems after I get rid of one bug another pops up.

One of my local variables in a dialog gets overwritten somehow and screws up my programs execution.  The really weird thing is that if I change the order of the variables the bug disappears!

I open the dialog from a previous dialog with this:

invoke DialogBoxParam, hInstance, IDD_MYDIALOG, hFirstDialog, addr NewDialog, 0

And the dialog proc looks something like this:

NewDialog proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL buffer[256]:BYTE         ;why does changing the order of these
LOCAL hCreateEdit:HWND ;cause hCreateEdit to be overwritten????

.if uMsg==WM_INITDIALOG
push hWnd
pop hMyDialog
invoke GetDlgItem, hMyDialog, IDC_CREATE_EDIT
mov hCreateEdit, eax
invoke RtlZeroMemory, addr buffer, 256
.elseif uMsg==WM_COMMAND

.if wParam == IDC_CREATE_OK
invoke SendMessage, hCreateEdit, WM_GETTEXTLENGTH, NULL, NULL
.if eax == 0 ;the user entered no text
fn MessageBox, hMyDialog, "Please enter some text.", "Error:", MB_OK or MB_ICONERROR
.else
invoke GetDlgItemText, hMyDialog, IDC_CREATE_EDIT, addr buffer, 256
invoke insertdata, addr buffer
.if eax == 1 ;insert was a success
invoke EndDialog, hMyDialog, NULL
mov eax, TRUE
ret
.endif
.endif
.endif

.elseif uMsg==WM_CLOSE ; if the user closes our dialog
invoke EndDialog, hMyDialog, NULL

.else
mov eax, FALSE
ret
.endif
mov eax, TRUE
ret
NewDialog endp


The way that it is written works fine.  BUT if I change the order of the local variables to:

LOCAL hCreateEdit:HWND
LOCAL newtagname[256]:BYTE


The variable hCreateEdit gets overwritten by some system dlls according to ollydbg.  Does anyone know what would cause a problem like this and why it is solved by changing the order of the variables??

qWord

Custos ,
do you know the difference between local and global/static variables?
FPU in a trice: SmplMath
It's that simple!

Custos

QuoteCustos ,
do you know the difference between local and global/static variables?

Eh, I think so... What are you trying to tell me?  :toothy
I know I can make it a global and I won't have the problem but why does the order of the variables matter in this case?  Or should I not be using one in this instance?

donkey

Hi Custos,

The problem is that you have declared hCreateEdit as a local variable and tried to use it as a global. The DialogProc is a callback function so any locals are discarded once a message is processed and RET is executed. In your procedure you have loaded hCreateEdit in the WM_INITDIALOG message handler, it's data is no longer valid as soon as that handler is completed so when you attempt to use it in the WM_COMMAND handler it's no longer useable. Since you are using a control on the dialog I would suggest that you discard hCreateEdit completely:

change:

invoke SendMessage, hCreateEdit, WM_GETTEXTLENGTH, NULL, NULL

to

invoke SendDlgItemMessage, hWnd, IDC_CREATE_EDIT, WM_GETTEXTLENGTH, NULL, NULL

This will negate any worries about whether the handles are valid or not.
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

Custos

Ah! I had totally forgot about locals being discarded in a dialog callback like that.  Thank you Donkey for your excellent reply.  I will try not to make this mistake again.  :U

One last question:

I am assuming the sqlite include file does not require these entries:

From the C header file:
typedef void (*sqlite3_destructor_type)(void*);
#define SQLITE_STATIC      ((sqlite3_destructor_type)0)
#define SQLITE_TRANSIENT   ((sqlite3_destructor_type)-1)

;/*
;** CAPI3REF: Constants Defining Special Destructor Behavior
;**
;** These are special values for the destructor that is passed in as the
;** final argument to routines like [sqlite3_result_blob()].  ^If the destructor
;** argument is SQLITE_STATIC, it means that the content pointer is constant
;** and will never change.  It does not need to be destroyed.  ^The
;** SQLITE_TRANSIENT value means that the content will likely change in
;** the near future and that SQLite should make its own private copy of
;** the content before returning.
;**
;** The typedef is necessary to work around problems in certain
;** C++ compilers.  See ticket #2191.

;*/

Am I correct in assuming that they are not necessary since we are not compiling the source?