I'm trying to use the 'fputs' function from CRTDLL.DLL to write to STDOUT w/o the newline added by 'puts'. The 'puts' function is working fine. I can't find the right value to pass to 'fputs 'to indicate STDOUT.
Posted on 2003-05-05 10:06:36 by gfalen
It's "stdout", not "STDOUT" - ie, a variable, not a constant... I assume crtdll.dll exports this variable though?
Posted on 2003-05-05 10:21:49 by f0dder
there... there should be a _iob export from crtdll.dll, which is an array of FILE structures.
#define stdin (&_iob[0])
#define stdout (&_iob[1])
#define stderr (&_iob[2])
Posted on 2003-05-05 10:26:35 by f0dder
So how to declare it? I tried

FILE struc
byte 18 dup(?)
FILE ends
externdef _iob:FILE

I get ...
Assembling: E:\Lang\Masm32\Example\My\Tabedit 1\Tabedit.asm
LINK : fatal error LNK1104: cannot open file 'RICHEDIT20.lib'

The .lib is there and it builds fine w/o the externdef?
Posted on 2003-05-05 12:30:47 by gfalen
I've just done a test with CRTDLL and it worked. Thats my test prog


main proc c

mov eax, stdin
add eax, sizeof FILE
invoke fputs, CStr(<"Hello, world",0Ah>), eax

main endp


and stdin is defined as:

externdef c _imp___iob:dword
stdin equ <_imp___iob>

So this should compile and link without errors. But, as you may notice, there is no start point in the source,
because startup code is from C runtime. See the .MAP file:

Timestamp is 3eb76953 (Tue May 06 09:50:43 2003)

Preferred load address is 00400000

Start Length Name Class
0001:00000000 000000ecH .text CODE
0002:00000000 00000024H .idata$5 DATA
0002:00000024 0000000eH .rdata DATA
0002:00000034 00000028H .idata$2 DATA
0002:0000005c 00000014H .idata$3 DATA
0002:00000070 00000024H .idata$4 DATA
0002:00000094 00000068H .idata$6 DATA
0002:000000fc 00000000H .edata DATA
0003:00000000 00000004H .CRT$XCA DATA
0003:00000004 00000004H .CRT$XCZ DATA
0003:00000008 00000004H .CRT$XIA DATA
0003:0000000c 00000004H .CRT$XIZ DATA
0003:00000010 00000004H .CRT$XPA DATA
0003:00000014 00000004H .CRT$XPZ DATA
0003:00000018 00000004H .CRT$XTA DATA
0003:0000001c 00000004H .CRT$XTZ DATA
0003:00000020 00000004H .data DATA

Address Publics by Value Rva+Base Lib:Object

0001:00000000 _main 00401000 f testj.obj
0001:00000018 _mainCRTStartup 00401018 f crtdll:crtexe.obj
0001:00000054 __cinit 00401054 f crtdll:crt0dat.obj
0001:00000079 __cexit 00401079 f crtdll:crt0dat.obj
0001:000000c0 _exit 004010c0 f crtdll:exit.obj
0001:000000ca __exit 004010ca crtdll:exit.obj
0001:000000d4 __initterm 004010d4 f crtdll:crtdll.dll
0001:000000da _free 004010da f crtdll:crtdll.dll
0001:000000e0 __flushall 004010e0 f crtdll:crtdll.dll
0001:000000e6 _ExitProcess@4 004010e6 f kernel32:KERNEL32.dll
0002:00000000 __imp__ExitProcess@4 00402000 kernel32:KERNEL32.dll
0002:00000004 \177KERNEL32_NULL_THUNK_DATA 00402004 kernel32:KERNEL32.dll
0002:00000008 __imp___initterm 00402008 crtdll:crtdll.dll
0002:0000000c __imp__free 0040200c crtdll:crtdll.dll
0002:00000010 __imp___flushall 00402010 crtdll:crtdll.dll
0002:00000014 __imp____GetMainArgs 00402014 crtdll:crtdll.dll
0002:00000018 __imp__fputs 00402018 crtdll:crtdll.dll
0002:0000001c __imp___iob 0040201c crtdll:crtdll.dll
0002:00000020 \177crtdll_NULL_THUNK_DATA 00402020 crtdll:crtdll.dll
0002:00000034 __IMPORT_DESCRIPTOR_crtdll 00402034 crtdll:crtdll.dll
0002:00000048 __IMPORT_DESCRIPTOR_KERNEL32 00402048 kernel32:KERNEL32.dll
0002:0000005c __NULL_IMPORT_DESCRIPTOR 0040205c crtdll:crtdll.dll
0003:00000000 ___xc_a 00403000 crtdll:cinitexe.obj
0003:00000004 ___xc_z 00403004 crtdll:cinitexe.obj
0003:00000008 ___xi_a 00403008 crtdll:cinitexe.obj
0003:0000000c ___xi_z 0040300c crtdll:cinitexe.obj
0003:00000010 ___xp_a 00403010 crtdll:cinitexe.obj
0003:00000014 ___xp_z 00403014 crtdll:cinitexe.obj
0003:00000018 ___xt_a 00403018 crtdll:cinitexe.obj
0003:0000001c ___xt_z 0040301c crtdll:cinitexe.obj
0003:00000020 __onexitbegin 00403020 crtdll:crt0dat.obj

So maybe fputs needs some initialization and you possibly have to call _cinit before using fputs.

Posted on 2003-05-06 03:01:37 by japheth
Forgot to mention that FILE struct I used is different (and larger in size):

FILE struct
_ptr dd ? ;+0
_cnt dd ? ;+4
_base dd ? ;+8
_flag dd ? ;+12
_file dd ? ;+16
_charbuf dd ? ;+20
_bufsiz dd ? ;+24
_tmpfname dd ? ;+28
FILE ends
Posted on 2003-05-06 03:28:20 by japheth
Very good stuff. However my crtdll.lib seems to be missing the _imp__iob
export. Can you help with this?

Assembling: E:\Lang\Masm32\Example\My\Crtdll\mpp.asm
mpp.OBJ : error LNK2019: unresolved external symbol __imp___iob referenced in function _main@0
mpp.exe : fatal error LNK1120: 1 unresolved externals

P.S how about this

externdef c _imp___iob:FILE
stdin equ _imp___iob
Posted on 2003-05-06 03:58:55 by gfalen
Regretably I cant remember exactly how I build the crtdll.lib I used.

I'll post it with this reply, so you may try it out.
Posted on 2003-05-06 06:38:05 by japheth
Works like a charm much thanks! So iob holds ptr's to FILES?
Posted on 2003-05-06 06:51:41 by gfalen
hm, I thought it was an array of FILEs, not an array of FILE pointers?
Posted on 2003-05-06 06:53:35 by f0dder
Refering to Japeth's code above - which works with the new lib file

mov eax, stdin ; must be ptr to FILE array
add eax, sizeof FILE ; 2'nd FILE is stdout
Posted on 2003-05-06 07:00:46 by gfalen
japheth gets __imp__iob - not _iob. Just like with function imports, the "__imp_" value is a pointer to the "real thing".

So, __imp__iob is a pointer to _iob (iob in C), which is an array of FILEs (not FILE*).
Posted on 2003-05-06 07:04:54 by f0dder
That's what i meant. _imp__iob holds a ptr to the FILE array. The 1'st is stdin, 2'nd is stdout, 3'rd is probably srderr - ?
Posted on 2003-05-06 07:09:41 by gfalen
:-) from a previous post in this thread (-:

#define stdin (&_iob[0])
#define stdout (&_iob[1])
#define stderr (&_iob[2])
Posted on 2003-05-06 07:12:06 by f0dder
Here's my crtdll.inc file for anyone who's interested. Let me know if you need examples of how to use it.

Oops - forgot to give you the invok macro

@str macro _str:vararg ; allows literals in invok
local lbl
$t catstr <_str>
if @InStr(1, %$t, <!">)
$_0 = $
_DATA segment
lbl byte _str, 0, 0
_DATA ends
exitm <offset lbl>
exitm <_str>

invok macro _func:req, _args:vararg
$invok catstr <_func>
if @InStr(1, <%$invok>, <impcall>)
_func _args
elseifb <_args>
invoke _func
$invok equ <>
irp $v, <_args>
$invok catstr $invok, <,>, @str($v) ; process literal
invoke _func $invok

@invok macro _func:req, _args:vararg
invok _func, _args
exitm <eax>
Posted on 2003-05-06 07:17:57 by gfalen
Here is a simple example which 'should' work with the above macros AND the crtdll.inc I posted.

tokenize proc pstr
nl db 13, 10, 0

strtok pstr, " "
.while eax
printf "%s%s", eax, addr nl
strtok 0, " "
tokenize endp

; usage:
; invok tokenize, "one two three"
Posted on 2003-05-06 08:00:11 by gfalen
Here is a web page which documents most of the crtdll interface.

Posted on 2003-05-06 08:28:10 by gfalen
qsort demo

max equ 1000 ;* 1000 ;* 5

ptable dd ?
_nl db 13, 10, 0

qsproc proc C, p1:ptr, p2:ptr
mov eax, p1 ; get array ptr 1
mov eax, [eax] ; get dword
mov edx, p2 ; get array ptr 2
sub eax, [edx]
qsproc endp

malloc 4 * (max+1)
mov ptable, eax
srand max

puts "Initialing..."
mov esi, 0
mov ebx, ptable
.while esi < max
mov [ebx][esi*4], eax
printf "%8u", eax
inc esi
puts addr _nl

printf "Sorting %u elements...%s", max, addr _nl
qsort ptable, max, 4, @ qsproc

mov esi, 0
mov edi, 0
mov ebx, ptable

.while esi < max -1
mov eax, [ebx][esi*4]
.if eax > [ebx][esi*4 +4]
inc edi
printf "%8u", eax
inc esi
puts addr _nl

printf "%u items out of order%s", edi, addr _nl
dispose ptable
Posted on 2003-05-06 09:14:29 by gfalen

Nice idea of using the C run-time dll :alright:
Posted on 2003-05-06 09:22:51 by Vortex