Hi,

does anyone know of how to support this feature with MASM? No problem with VC. Obviously a flags in the section records of the .OBJ file has to be set. And a special section to be written? Any clues here would be apreciated.

Japheth
Posted on 2003-01-02 05:14:44 by japheth
http://www.launcherasm.com/technical/comdats.html

The segment directive does have a COMDAT flag, but I have not have a need to play with it. The docs I've read state that only the first segment is included when the names are the same and the COMDAT flag is set.
Posted on 2003-01-02 07:41:47 by bitRAKE
Hi bitRAKE


The segment directive does have a COMDAT flag, but I have not have a need to play with it. The docs I've read state that only the first segment is included when the names are the same and the COMDAT flag is set.


That's not the "feature" im interested in. I want to extract single function definitions from one .OBJ file. In VC thats achieved with option /Gy.

I have one file with many small functions, and want to link some of them into my exe. In VC it works, but looks pretty bad (see code below). But not found a way yet to get the same results with MASM. Possible it cannot be done (without extra tools)




#include <windows.h>

extern __stdcall InvokeHelper(void);

void __declspec( naked ) Range_get_Application(LPDISPATCH pDispatch, PVOID arg1)
{
_asm{
call InvokeHelper
ret 8
_emit 0 //dw numArgs
_emit 0
_emit (VT_USERDEFINED or VT_BYREF) and 0FFh //dw rettype
_emit (VT_USERDEFINED or VT_BYREF) shr 8

_emit DISPATCH_PROPERTYGET //dw functype
_emit 0

_emit 94h //dd dispid
_emit 0
_emit 0
_emit 0

_emit 4 // size returnparm
_emit 0
};

}
void __declspec( naked ) Range_get_Item(LPDISPATCH pDispatch, VARIANT arg1, VARIANT arg2, PVOID arg3)
{
_asm{
call InvokeHelper
ret 40
_emit 2 //dw numArgs
_emit 0
_emit VT_VARIANT //dw type arg1
_emit 0
_emit VT_VARIANT //dw type arg2
_emit 0
_emit (VT_VARIANT or VT_BYREF) and 0FFh //dw rettype
_emit (VT_VARIANT or VT_BYREF) shr 8

_emit DISPATCH_PROPERTYGET //dw functype
_emit 0

_emit 0AAh //dd dispid
_emit 0
_emit 0
_emit 0

_emit 16 // size returnparm
_emit 0
};
}
void __declspec( naked ) Range_put_Item(LPDISPATCH pDispatch, VARIANT arg1, VARIANT arg2, VARIANT arg3)
{
_asm{
call InvokeHelper
ret 52
_emit 3 //dw numArgs
_emit 0
_emit VT_VARIANT //dw type arg1
_emit 0
_emit VT_VARIANT //dw type arg2
_emit 0
_emit VT_VARIANT //dw type arg3
_emit 0
_emit 0 //dw rettype
_emit 0

_emit DISPATCH_PROPERTYPUT //dw functype
_emit 0

_emit 0AAh //dd dispid
_emit 0
_emit 0
_emit 0

_emit 0 // size returnparm
_emit 0
};
}

Posted on 2003-01-02 09:54:30 by japheth
The assembler must tell the linker where, in the .OBJ file, each function starts and ends. If the assembler doesn't provide this information, it cannot be done.

The old fashioned way is to put each function in a separate .ASM file, generate the separate .OBJ files, and then combine the .OBJ files into a single .LIB file.
Posted on 2003-01-02 17:05:30 by tenkey
tenkey,

thanks for the reply. Im aware of that "old way" possibility, but didnt want to generate 6500 small ASM files :grin: .

But priority of that item has diminished in the meantime.
Posted on 2003-01-02 19:45:11 by japheth
Heres a nice addon for your MASM32 bin: LIBM.EXE :grin:

This will take an *.asm file and chop / build a static lib for function level linking (as this thread is about).

There are five different 'labels' you need to put in your asm file for this purpose:

;#HEADER START - This is a universal header section (.386 / include's etc)

;#HEADER END - This is the end of header label. It will abort if not found.

;#COFF_PROC FunctionName - This is the start of a unique function to linked on a per call basis

;#COFF_ENDP - This is the end of coff function section. It wil get grumpy if not found ;)

;#PROTO Prototype_line_for_function - This must be within the scope of the #COFF lables. It too will make the tool complain if its not found ;)

So with all this in mind, here is a samle file:
;----------------------------------------


;#HEADER START
.386
.model flat,stdcall
option casemap:none

; Any includes and Libs used

;#HEADER END

;----------------------------------------

;#COFF_PROC One
;#PROTO One PROTO
.code
One PROC
nop
ret
One ENDP
;#COFF_ENDP

;----------------------------------------

;#COFF_PROC Two
;#PROTO Two PROTO
.code
Two PROC
nop
nop
ret
Two ENDP
;#COFF_ENDP

;----------------------------------------
end


By the way it is designed, you can build and test your functions before hand and when decidedly finished, you simply pass it thru LIBM to make it into the static lib. It outputs into the calling directory..

As an added bonus, it also generates an output include header for your lib!
You will find the include in the same directory as the generated lib.

Please try it out, and give me feedback. I will open source it, eventually. But i want to get it right first ;)

There is only one area of noticeable problem. The ShellExecute API is not syncronized for commandline functions. So i have made a defualt (time-out), which doesnt make me sit well... :rolleyes: . I havent found a way of making my app wait until ShellExecute finishes. I searched the board and got two possible solutions:

1) Use WaitForSingleObject. But the returned 'handle' by ShellExe is not a 'real' instance handle (as the MSDN indicates, and proven empirically). So this fails.

2) Use the "shell" function in the masm package. I searched the entire M32Lib dir and found nothing of it. (It was a comment made by Hutch in an old thread on a related topic).

I guess the only other solution is going multi-threading (somehow), still cant see how it would be any advantage since the ShellExe is not sychronized anyways...

Anywho, for now, the time-out is happily balanced between 'fast-enough' and 'still-gets-the-job-done' ;)

Let me know if you have any thoughts you can add to this. I would like to use something more trusty than a delay...

Anywho.. Enjoy!
:alright:
NaN
Posted on 2003-01-04 04:39:49 by NaN
Ok.. I think i got it all working perfect now...

No delays!! And i fixed a bug with LineFeeds....

I took half the MASM32.INC's from the M32LIB dir and made one big file, and it works every time!

So I feel alot more confident it will work for you ;)
One thing i noticed tho, Ensue your include / includelib paths in the header section have a drive letter indicated.

It works by setting up a WIndows/temp directory, which may not be the same drive as your masm32 package.

And dont worry, Everytime its ran, i programmed it to empty the WINDOW\TEMP\MASMLIB directory first. If its not there to begin with, i programmed it to make one.. So if you want to look at the generated files, look there ;)

Enjoy! (Please give feedback)
:alright:
NaN
Posted on 2003-01-04 05:47:35 by NaN
The attachment ( ;) )

.:EDIT:. Removed outdated attachment. See end of post.
Posted on 2003-01-04 06:21:23 by NaN
NaN,

This is the "shell" procedure I did some time ago, it seems to work OK.


; #########################################################################

.486 ; create 32 bit code
.model flat, stdcall ; 32 bit memory model
option casemap :none ; case sensitive

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc

.code

; ########################################################################

shell proc lpfilename:DWORD

LOCAL xc :DWORD ; exit code

.data
st_info STARTUPINFO <0>
pr_info PROCESS_INFORMATION <0>
.code

invoke CreateProcess,NULL,lpfilename,NULL,NULL,
NULL,NULL,NULL,NULL,
ADDR st_info,
ADDR pr_info

; -------------------------------------------
; loop while created process is still active
; -------------------------------------------
@@:
invoke GetExitCodeProcess,pr_info.hProcess,ADDR xc
cmp xc, STILL_ACTIVE
je @B

ret

shell endp

; ########################################################################

Regards,

hutch@movsd.com
Posted on 2003-01-04 07:10:36 by hutch--
that doesn't look threadsafe though. Ok, your startupinfo is zeroed out and you're not using the process_information, so theoretically that doesn't matter; but I think it's more optimal to WaitForSingleObject instead of looping GetExitCodeProcess? Also, PSDK says you ought to CloseHandle the stuff in the process_information structure. I dunno if these handles are freed when the target app stops executing, or if they linger until your app stops - but there's a potential for leaking there. One more thing. This *might* just be me remembering wrong, or it might be some windows versions that are more picky than others - but I think you ought to set at least startupinfo.cb (or whatever the size-of-structure member is called in the masm32 includes).
Posted on 2003-01-04 09:45:56 by f0dder
Hi NaN,

thats a very good idea with your libm tool. I tried it out and it worked. 2 things remain to mention:

- possibly libm could say one word or two about where it has written its stuff. It took me some time to find out where the output stuff has been written :stupid: .
- I would prefer these #proto statements to be optional. Currently libm complains if it founds none, but if I delete all except one, it runs without errors giving strange results.

:alright:

Japheth



After some time I realized that every time your tool comes to this "finished" message, a invisible "msdos" or "winoa386" process remains running, filling the process list with this things. So there goes something wrong.
(using WIN98 SE, starting libm from the command line)
Posted on 2003-01-04 10:37:11 by japheth
Thanx Japheth, i will add some command line switches (and look into the 'proto' bug)...

However, im more concerned with the ugly by product of process trash. I didnt notice this, but after retesting i too get a "winoldapp" in my process list :rolleyes: .

So to save time and hopefully get some expert help, how *should* i be calling an external process. The external process is calling a batch file. I generate dynamically in the Temp director (mentioned above (\TEMP\MASMLIB\)) the batch file everytime, and run it as follows:
@Finished:

;------------------------------------------- Build the output bat file
DESTROY hINC
lea edx, tempDir
add edx, cPath
xor eax, eax
mov [edx], eax
lea edx, buffer
mov [edx], eax
invoke szCatStr, addr buffer, addr tempDir
invoke szCatStr, addr buffer, CStr("MAKE.BAT")

lea esi, buffer
mov hINC, $NEW( CFile, esi,CFILE_WRITE, 128 )
; Open: \WINDOWS\TEMP\MAKE.BAT

; Compile Line...
xor eax, eax
lea edx, buffer
mov [edx], eax
invoke szCatStr, addr buffer, CStr("ML.EXE /c /coff ")
invoke szCatStr, addr buffer, addr tempDir
invoke szCatStr, addr buffer, CStr("*.ASM")
invoke szCatStr, addr buffer, CStr(<<13,10,13,10>>)
METHOD hINC, CFile, WriteLine, addr buffer
; Writeline: ML.EXE /c /coff C:\WINDOWS\TEMP\MASMLIB\*.ASM

; Lib Line...
xor eax, eax
lea edx, buffer
mov [edx], eax
invoke szCatStr, addr buffer, CStr("LIB.EXE ")
invoke szCatStr, addr buffer, addr tempDir
invoke szCatStr, addr buffer, CStr("*.OBJ /out:")
invoke szCatStr, addr buffer, addr tempDir
invoke szCatStr, addr buffer, addr fname
invoke szCatStr, addr buffer, CStr(".LIB")
invoke szCatStr, addr buffer, CStr(<<13,10,13,10>>)
METHOD hINC, CFile, WriteLine, addr buffer
; WriteLine: LIB.EXE C:\WINDOWS\TEMP\MASMLIB\*.OBJ /out:C:\WINDOWS\TEMP\MASMLIB\name.lib


; Copy line
xor eax, eax
lea edx, buffer
mov [edx], eax
invoke szCatStr, addr buffer, CStr("COPY ")
invoke szCatStr, addr buffer, addr tempDir
invoke szCatStr, addr buffer, addr fname
invoke szCatStr, addr buffer, CStr(".* ")
invoke szCatStr, addr buffer, addr apath
invoke szCatStr, addr buffer, CStr("*.*")
invoke szCatStr, addr buffer, CStr(<<13,10,13,10>>)
METHOD hINC, CFile, WriteLine, addr buffer
; WriteLine: COPY C:\WINDOWS\TEMP\MASMLIB\name.* D:\app_path\*.*

DESTROY hINC
xor eax, eax
mov hINC, eax
; Close output writing file.

;------------------------------------------- Build the Lib
xor eax, eax
lea edx, buffer
mov [edx], eax
invoke szCatStr, addr buffer, addr tempDir
invoke szCatStr, addr buffer, CStr("MAKE.BAT")

invoke ShellExecute, NULL, NULL, addr buffer, NULL, addr tempDir, SW_HIDE
invoke StdOut, CStr(<<13,10,"Finished!...">>)


Since its a console mode app, i dont have a window 'parent' handle, so i placed NULL in its place. Perhaps this is why its doing this?

Does anyone have a better solution? (Im new in these areas of the Windows API ;) ) I just know how to use the MSDN, thats all ;)

(( Perhaps i should make it a dialog box window? ))
Anyone, please give me your thoughts.. thanx in advance.
:alright:
NaN
Posted on 2003-01-04 12:44:39 by NaN
CreateProcess. It's more flexible anyway. Remember to close Thead and Process
handles when you're done using them. It's probably a good idea to create a simple wrapper around CreateProcess, so you don't have to specify the long parameter list CreateProcess takes, every time you want to call an app. You can use hutches shell proc above as a template.
Posted on 2003-01-04 12:53:36 by f0dder
Thanx f0dder,

I didnt think i should use it, cause of your above concerns... I figured since you found *some* flaw i would wait till some conclusion had arrived...

I will consult the MSDN and the example and see what i can produce ;) I agree a wrapper would be good.. and would make a good lib entry as well ;)

Hmm.. I wonder what good the ShellExecute is for then.. (besides web links and printing documents... :rolleyes: ). Ah well i thought it was too ez for M$ standards anyways ;)

On the lighter side, I got the PROTO bug fixed.. I didnt limit the search for PROTO's to within the scope.. It was finding PROTO statements outside of the function scope, and moving happily along.

:alright:
NaN
Posted on 2003-01-04 14:06:46 by NaN
NaN, it's not much work creating a good wrapper around CreateProcess. Put the two required structures as LOCAL variables. Set their sizes. Fill the startupinfo with GetStartupInfo (and perhaps modify some members). Do the CreateProcess call, and if you want the wrapper to wait until app is done executing, WaitForSingleObject on the pi.hProcess. Remember to CloseHandle pi.hProcess and pi.hThread (you can GetExitCodeProcess on pi.hProcess after the process is done executing, so I assume the kernel mode object for at least the hProcess isn't automatically freed until _your_ app stops - and we don't like leaks, do we?).

ShellExecute is ok for quick and dirty stuff, but it's not very flexible. You could code your own ShellExecute, but that's a somewhat larger project and involves quite some mucking around in the registry to find filetype associations etc.
Posted on 2003-01-04 14:15:52 by f0dder
Hi NaN,

This is my tool to run exe/bat file from dos. It is not optimize yet but it works in ME & 2K

It also doesn't leave the winoldap


To run it:
e.g:
- RunDOS notepad readme.txt
- RunDOS Make.bat

for long filename:
in W2K -> RunDOS notepad readme first.txt
in W9X -> RunDOS notepad "readme first.txt"



; ####################################
.586
.model flat, stdcall
option casemap :none ; case sensitive

; ####################################
include \masm32\include\windows.inc
include \masm32\include\masm32.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include clash.inc

includelib \masm32\lib\user32.lib
includelib \masm32\lib\masm32.lib
includelib \masm32\lib\kernel32.lib
includelib clash.lib

Main PROTO
MyShell PROTO :LPSTR
IsNT PROTO

; ---------------------
; literal string MACRO
; ---------------------
literal MACRO quoted_text:VARARG
LOCAL local_text
.data
local_text db quoted_text,0
.code
EXITM <local_text>
ENDM

; --------------------------------
; string address in INVOKE format
; --------------------------------
SAddr MACRO quoted_text:VARARG
EXITM <ADDR literal(quoted_text)>
ENDM

; ####################################
.DATA?
CommandLine LPSTR ?

.CODE

START:

call Main
invoke ExitProcess,0

; ####################################

Main Proc USES ESI
Local Params[128]:BYTE, ln_Count:DWORD

invoke GetCommandLine
mov CommandLine, eax
invoke CL_ScanArgsX, CommandLine
.If (eax > 1)
mov ecx, eax
dec ecx
push ecx
mov dword ptr Params[0], 0
invoke IsNT
.If (eax == 1)
invoke lstrcat, Addr Params, SAddr("Cmd.exe /C """)
pop ecx
mov esi, 4
NextArg1:
push ecx
invoke lstrcat, Addr Params, CL_argv[esi]
pop ecx
.If (ecx > 1)
invoke lstrcat, Addr Params, SAddr(" ")
add esi, 4
.If (CL_argv[esi] == NULL)
mov ecx,1
.endif
.endif
loop NextArg1
invoke lstrcat, Addr Params, SAddr("""")

.else
invoke lstrcat, Addr Params, SAddr("Command.Com /C ")

pop ecx
mov esi, 4
NextArg2:
push ecx
invoke lstrcat, Addr Params, SAddr("""")
invoke lstrcat, Addr Params, CL_argv[esi]
invoke lstrcat, Addr Params, SAddr("""")
pop ecx
.If (ecx > 1)
invoke lstrcat, Addr Params, SAddr(" ")
add esi, 4
.endif
loop NextArg2
.endif

invoke MyShell, Addr Params
.endif

ret
Main EndP

; ####################################

MyShell proc tp_CommandLine:LPSTR
.Data
SInfo STARTUPINFO <0>
Pr_Info PROCESS_INFORMATION <0>

.Code
mov SInfo.cb, SizeOf STARTUPINFO
mov SInfo.dwFlags, STARTF_USESHOWWINDOW
mov SInfo.wShowWindow, SW_HIDE
invoke CreateProcess, NULL, tp_CommandLine, NULL, NULL,
FALSE, 0, NULL, NULL, Addr SInfo, Addr Pr_Info

; -------------------------------------------
; loop while created process is still active
; -------------------------------------------
.If (eax > 0)
@@:
invoke WaitForSingleObject, Pr_Info.hProcess, 200
cmp eax, WAIT_OBJECT_0
jne @B
.endif
invoke CloseHandle, Pr_Info.hProcess
invoke CloseHandle, Pr_Info.hThread
ret
MyShell endp

; ####################################

IsNT proc
Local vi:OSVERSIONINFO

mov vi.dwOSVersionInfoSize, SizeOf vi
invoke GetVersionEx, Addr vi ; eax
cmp eax, 0
je ErrorCheck

mov ecx, vi.dwPlatformId
cmp ecx, VER_PLATFORM_WIN32_NT
je Win_NT

mov eax, 0 ; Not Win NT
ret

Win_NT:
mov eax, 1 ; Win NT
ret

ErrorCheck:
mov eax, 2 ; Error

ret
IsNT endp

; ####################################

END START


The clash.inc & lib I took it from Jibz

Hope you can find it useful :)

Btw, I tried your tools and find a minor bug. My masm is located in drive E:
If in the program I have a code like "include \masm32\include\windows.inc" (lib also) the tools cannot build the lib.
But if a add a drive letter before it "include e:\masm32 \include\windows.inc", it builds the lib

I would also prefer if we don't have to put #COFF_ / #PROTO (search PROC automatically). It will be more useful. If it is difficult just ignore this one. But I still hoping...:)

:alright:
Posted on 2003-01-04 17:05:49 by HermanT
Here is a working, bug free (hopefully) version. I managed to put togther exactly what HermanT had (should have simply waited ;) ).

As well i modified the ;#PROTO usage. Its your options to use it. It will not complain at all if you dont add a single one. However, if you do, there is no checking.. only a simple cut&paste into an include file.

In future versions i will see how much overhead it would be to process the lines for PROC and ENDP statements. This will elimenate the ;#COFF_START and ;COFF_END statements. However, for now it is still in there ;)

As well, cause im using createprocess, it pipes output directly to you, so if you have a problem, its in your face ;)

Please try it out, and give me feedback... (i think HermanT's will be the next focus).
:alright:
NaN
Posted on 2003-01-04 20:14:51 by NaN
grrrrr.....:mad:
I knew my program has a bug, and I missed it (BUT HOW!!!) :o :grin:

NaN, don't use the code before, use this one instead! (attachment)

Now I already tried the new one. It is crash on my W2K.
If you already use the code above maybe that's what causing error.

Also i forgot to mentioned that when i used the old one (libm 1.3), the lib size is bigger than if compiled from make.bat directly.
What i mean here is, i use LIBM to create lib for my test program with 2 proc (trans.asm), the lib size is 1,286 bytes. Then I simply copy the make.bat from the temp directory into my program directory, and change it to compile the trans.asm directly. The lib size is 688 bytes!

I guess it's because the program separated into a smaller program (one proc by one proc) but the header is on every program including the data section. So the data section is duplicated. Then how if we have more than 2 proc ?? :confused:


This is the code for my test program
; Program trans.asm


;#HEADER START
.386
.model flat, stdcall ; 32 bit memory model
option casemap :none ; case sensitive
include \masm32\include\windows.inc

.Data
cMsg db 'Test',0
;#HEADER END


;#COFF_PROC Clear
;#PROTO Clear PROTO :DWORD
.Code
Clear Proc hWnd:DWORD
mov eax, 0
ret
Clear endp
;#COFF_ENDP

;#COFF_PROC Two
;#PROTO Two PROTO
.code
Two PROC
nop
nop
ret
Two ENDP
;#COFF_ENDP

End


And this is the rundos program (fixed)

Regards :alright:
Posted on 2003-01-04 22:41:36 by HermanT
Hello HermanT,


grrrrr.....:mad:
I knew my program has a bug, and I missed it (BUT HOW!!!) :o :grin:
NaN, don't use the code before, use this one instead! (attachment)
Now I already tried the new one. It is crash on my W2K.
If you already use the code above maybe that's what causing error.

Thanx for the testing.. I dont have an NT type OS, so this is good to know.. Thanx!
Also i forgot to mentioned that when i used the old one (libm 1.3), the lib size is bigger than if compiled from make.bat directly.
What i mean here is, i use LIBM to create lib for my test program with 2 proc (trans.asm), the lib size is 1,286 bytes. Then I simply copy the make.bat from the temp directory into my program directory, and change it to compile the trans.asm directly. The lib size is 688 bytes!

If i understood this correctly, then the reason is because you defeat the purpose of the LIBM. The extra size (i assume) is because the files are separate, and thus have their own COFF header section in the LIB ( 2 functions, 2 COFF headers). By copying out the make.bat (from the old v1.3 version), and compiling the origional source, it ends up with only one COFF section ( 2 functions, 1 COFF header). This means if you only want use of function1 in the lib, you have to have function 2 as extra 'filler' in your code, cause there is not separate COFF headers....

So as i see it:
1 COFF, 2 Functions == 688byte lib (non-function level linking)
2 COFF, 2 Functions == 1286byte lib (function level linking).

This is the solution to your test program.


;#HEADER START
.386
.model flat, stdcall ; 32 bit memory model
option casemap :none ; case sensitive
include \masm32\include\windows.inc

;#HEADER END


;#COFF_PROC Clear
;#PROTO Clear PROTO :DWORD
[b].Data
cMsg db 'Test',0[/b]

.Code
Clear Proc hWnd:DWORD
mov eax, 0
ret
Clear endp
;#COFF_ENDP

;#COFF_PROC Two
;#PROTO Two PROTO
[b].data
dataForTwoProc dd 0[/b]
.code
Two PROC
nop
nop
ret
Two ENDP
;#COFF_ENDP

End


And this is the rundos program (fixed)

Thanks i will look it over..
:alright:
NaN
Posted on 2003-01-05 00:44:15 by NaN
Correct me if im wrong here, but did you say that v1.5 crashed on NT?

Cause looking over your solution, all i see is you separating a prefixed "CMD.EXE" or "COMMAND.COM" if the version is NT or Not. The reason im puzzled is because i dont use either when calling an external process. I leave all this up to the OS to decide what to do. I simpy gave it the filename and arguments in a string.

Or perhaps your trying to tell me that i need to add "CMD.EXE /C " for NT stype OS's? Cause 9x version work well without it...

Thanx
:alright:
NaN
Posted on 2003-01-05 00:57:41 by NaN