InitializeCriticalSection fails in NASM

0

UPDATE: based on comments below, I revised the code below to add a struc and a pointer (new or revised code has "THIS IS NEW" or "THIS IS UPDATED" beside the code). Now the program does not crash, so the pointer is initialized, but the programs hangs at EnterCriticalSection. I suspect that in translating the sample MASM code below into NASM syntax, I did not declare the struc correctly. Any ideas? Thanks very much.

ORIGINAL QUESTION: Below is a simple test program in 64-bit NASM, to test a critical section in Windows. This is a dll and the entry point is Main_Entry_fn, which calls Init_Cores_fn, where we initialize four threads (cores) to call Test_fn.

I suspect that the problem is the pointer to the critical section. None of the online resources specifies what that pointer is. The doc "Using Critical Section Objects" at https://docs.microsoft.com/en-us/windows/desktop/sync/using-critical-section-objects shows a C++ example where the pointer appears to be relevant only to EnterCriticalSection and LeaveCriticalSection, but it's not a pointer to an independent object.

For those not familiar with NASM, the first parameter in a C++ signature goes into rcx and the second parameter goes into rds, but otherwise it should function the same as in C or C++. It's the same thing as InitializeCriticalSectionAndSpinCount(&CriticalSection,0x00000400) in C++.

Here's the entire program:

; Header Section
[BITS 64]
[default rel]

extern malloc, calloc, realloc, free
global Main_Entry_fn
export Main_Entry_fn
extern CreateThread, CloseHandle, ExitThread
extern WaitForMultipleObjects, GetCurrentThreadId
extern InitializeCriticalSectionAndSpinCount, EnterCriticalSection
extern LeaveCriticalSection, DeleteCriticalSection, InitializeCriticalSection

struc CRITICAL_SECTION ; THIS IS NEW
.cs_quad: resq 5
endstruc

section .data align=16
const_1000000000: dq 1000000000
ThreadID:  dq 0
TestInfo: times 20 dq 0
ThreadInfo: times 3 dq 0
ThreadInfo2: times 3 dq 0
ThreadInfo3: times 3 dq 0
ThreadInfo4: times 3 dq 0
ThreadHandles: times 4 dq 0
Division_Size: dq 0
Start_Byte: dq 0
End_Byte: dq 0
Return_Data_Array: times 4 dq 0
Core_Number: dq 0
const_inf: dq 0xFFFFFFFF
SpinCount: dq 0x00000400

CriticalSection: ; THIS IS NEW
istruc CRITICAL_SECTION
iend

section .text

; ______________________________________

Init_Cores_fn:

; Calculate the data divisions
mov rax,[const_1000000000]
mov rbx,4 ;cores
xor rdx,rdx
div rbx
mov [End_Byte],rax
mov [Division_Size],rax
mov rax,0
mov [Start_Byte],rax

; Populate the ThreadInfo arrays to pass for each core
; ThreadInfo:  (1) startbyte; (2) endbyte; (3) Core_Number
mov rdi,ThreadInfo
mov rax,[Start_Byte]
mov [rdi],rax
mov rax,[End_Byte]
mov [rdi+8],rax
mov rax,[Core_Number]
mov [rdi+16],rax

call DupThreadInfo ; Create ThreadInfo arrays for cores 2-4

mov rbp,rsp ; preserve caller's stack frame
sub rsp,56 ; Shadow space (was 32)

; _____
; Create four threads

label_0:

mov rax,[Core_Number]
cmp rax,0
jne sb2
mov rdi,ThreadInfo
jmp sb5
sb2:cmp rax,8
jne sb3
mov rdi,ThreadInfo2
jmp sb5
sb3:cmp rax,16
jne sb4
mov rdi,ThreadInfo3
jmp sb5
sb4:cmp rax,24
jne sb5
mov rdi,ThreadInfo4
sb5:

; _____
; Create Threads

mov rcx,0               ; lpThreadAttributes (Security Attributes)
mov rdx,0               ; dwStackSize
mov r8,Test_fn          ; lpStartAddress (function pointer)
mov r9,rdi              ; lpParameter (array of data passed to each core)
mov rax,0
mov [rsp+32],rax            ; use default creation flags
mov rdi,ThreadID
mov [rsp+40],rdi            ; ThreadID

call CreateThread

; Move the handle into ThreadHandles array (returned in rax)
mov rdi,ThreadHandles
mov rcx,[Core_Number]
mov [rdi+rcx],rax
mov rdi,TestInfo
mov [rdi+rcx],rax

mov rax,[Core_Number]
add rax,8
mov [Core_Number],rax
mov rbx,32 ; Four cores
cmp rax,rbx
jl label_0

mov rcx,CriticalSection ; THIS IS REVISED
mov rdx,[SpinCount]
call InitializeCriticalSectionAndSpinCount

; _____
; Wait

mov rcx,4 ;rax          ; number of handles
mov rdx,ThreadHandles       ; pointer to handles array
mov r8,1                ; wait for all threads to complete
mov r9,[const_inf]      ;4294967295 ;0xFFFFFFFF

call WaitForMultipleObjects

; _____

mov rsp,rbp ; can we push rbp so we can use it internally?
jmp label_900

; ______________________________________

Test_fn:

mov rdi,rcx

mov r14,[rdi] ; Start_Byte
mov r15,[rdi+8] ; End_Byte
mov r13,[rdi+16] ; Core_Number

;______
; while(n < 1000000000)

label_401:
cmp r14,r15
jge label_899

mov rcx,CriticalSection
call EnterCriticalSection

; n += 1
add r14,1

mov rcx,CriticalSection
call LeaveCriticalSection

jmp label_401

;______

label_899:

mov rdi,Return_Data_Array
mov [rdi+r13],r14

mov rbp,ThreadHandles
mov rax,[rbp+r13]

call ExitThread

ret

; __________

label_900:

mov rcx,CriticalSection
call DeleteCriticalSection

mov rdi,Return_Data_Array
mov rax,rdi

ret

; __________
; Main Entry

Main_Entry_fn:
push rdi
push rbp
call Init_Cores_fn
pop rbp
pop rdi
ret

DupThreadInfo:
mov rdi,ThreadInfo2
mov rax,8
mov [rdi+16],rax ; Core Number
mov rax,[Start_Byte]
add rax,[Division_Size]
mov [rdi],rax
mov rax,[End_Byte]
add rax,[Division_Size]
mov [rdi+8],rax
mov [Start_Byte],rax

mov rdi,ThreadInfo3
mov rax,16
mov [rdi+16],rax ; Core Number
mov rax,[Start_Byte]
mov [rdi],rax
add rax,[Division_Size]
mov [rdi+8],rax
mov [Start_Byte],rax

mov rdi,ThreadInfo4
mov rax,24
mov [rdi+16],rax ; Core Number
mov rax,[Start_Byte]
mov [rdi],rax
add rax,[Division_Size]
mov [rdi+8],rax
mov [Start_Byte],rax

ret

The code above shows the functions in three separate places, but of course we test them one at a time (but they all fail).

To summarize, my question is why do InitializeCriticalSection and InitializeCriticalSectionAndSpinCount both fail in the code above? The inputs are dead simple, so I don't understand why it should not work.

windows
multithreading
nasm
asked on Stack Overflow Mar 29, 2019 by RTC222 • edited Mar 29, 2019 by RTC222

1 Answer

1

InitializeCriticalSection take pointer to critical section object

The process is responsible for allocating the memory used by a critical section object, which it can do by declaring a variable of type CRITICAL_SECTION.

so code can be something like (i use masm syntax)

CRITICAL_SECTION STRUCT
    DQ 5 DUP(?)
CRITICAL_SECTION ends

extern __imp_InitializeCriticalSection:QWORD
extern __imp_InitializeCriticalSectionAndSpinCount:QWORD

.DATA?
CriticalSection CRITICAL_SECTION {}

.CODE 

lea rcx,CriticalSection
;mov edx,400h
;call __imp_InitializeCriticalSectionAndSpinCount
call __imp_InitializeCriticalSection

also you need declare all imported functions as

extern __imp_funcname:QWORD

instead

extern funcname
answered on Stack Overflow Mar 29, 2019 by RbMm

User contributions licensed under CC BY-SA 3.0