The MASM Forum

Miscellaneous => 16 bit DOS Programming => Topic started by: ggmasm32 on September 20, 2015, 08:18:42 AM

Title: linking obj file generated by VC cl with linker16
Post by: ggmasm32 on September 20, 2015, 08:18:42 AM
Ok I hoped the extern question here, I put last was last, but it looks like I am becoming a prime example of "shake one's hand today and shake one's wrist tomorrow" meaninig you help one person again he will ask even more :P

Here is hopefully the last question and hope it will get you challenged:
The two c files I can compile simply by cl <file1>.c <file2>.c and it will link its own 32-bit linker automatically and will generate executable fine.
But my objective is to be able to link obj file generated by both asm and C codes and generated one file in order to continue developing in both C and asm and mix them together. So first step of doing is compile the c file to generate only object file using /c (with no link) and explicitly link with link16.exe. Now I am getting below, I am still digging around but if you got something on top of your head, I will greatly appreciate it!!


C:\git\minix.dev\exp>nmake allc

Microsoft (R) Program Maintenance Utility Version 9.00.21022.08
Copyright (C) Microsoft Corporation.  All rights reserved.

        cl /c /W3 cfile.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.21022.08 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.
cfile.c
cfile.c(17) : warning C4013: 'extFcnC' undefined; assuming extern returning int
cfile.c(5) : warning C4101: 'result' : unreferenced local variable
        cl /c /W3 libc.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.21022.08 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.
libc.c
libc.c(5) : warning C4101: 'result' : unreferenced local variable
        link16 cfile.obj libc.obj,,,,,

Microsoft (R) Segmented Executable Linker  Version 5.60.339 Dec  5 1994
Copyright (C) Microsoft Corp 1984-1993.  All rights reserved.

cfile.obj : fatal error L1101: invalid object module
Object file offset: 1 Record type: 4c
NMAKE : fatal error U1077: 'C:\masm32\bin\link16.EXE' : return code '0x2'
Stop.

C:\git\minix.dev\exp>

Title: Re: linking obj file generated by VC cl with linker16
Post by: dedndave on September 20, 2015, 08:29:40 AM
32-bit windows uses COFF format OBJ files
16-bit DOS uses OMF format OBJ files

the two are not compatible
Title: Re: linking obj file generated by VC cl with linker16
Post by: dedndave on September 20, 2015, 08:32:40 AM
try
cl /help
or
cl /?

to see if it shows command-line options
if you are, indeed, compiling 16-bit C code, it may allow a /omf switch to generate OMF OBJ's
Title: Re: linking obj file generated by VC cl with linker16
Post by: ggmasm32 on September 20, 2015, 01:07:19 PM
ok i did some investigation on this. thanks for help.
it seems like omf is old format and cl does not even list for that switch when i do cl /?
i even downgraded to vstudio 2003. Not much
I did supply /omf switch anyways but the file generated still seems to be an COFF format. How did I know?
beginning signature is 4C. For OMF files I'd expect one of the supported format should be 80h at the beginning. THat is what I see in the asm generated file and also from object model format specification.

I am on the hunt for C compiler that can generate omf format. I actually found Agner's objconv.exe tool which is pretty handy and it can convert from COFF to OMF.
The trouble is seems like it is removing some information from file:
the printf() statement will now cause error when I do the output of this utility.
Also program has no entry when I link.
I decided to dump this approach.

Hunt for the C compiler that can generate omf.
Title: Re: linking obj file generated by VC cl with linker16
Post by: dedndave on September 20, 2015, 01:59:04 PM
i seem to recall someone saying that turbo-c ver 2.0 was out there for free
to get microsoft, i think the last 16-bit version of their compiler was MSC v 6.0 or something
Title: Re: linking obj file generated by VC cl with linker16
Post by: Vortex on September 20, 2015, 05:41:16 PM
Hi ggmasm32,

You cannot link together 32 bit and 16 bit object modules even if they share the same format COFF or OMF. You need a 16 bit C\C++ compiler :

http://digitalmars.com/download/freecompiler.html
Title: Re: linking obj file generated by VC cl with linker16
Post by: FORTRANS on September 20, 2015, 11:38:36 PM
Hi,

   You could participate in the "Smaller C" thread by Alexei A. Frounze
in the usenet group alt.os.development.  He is making a C compiler
for his own use.

Regards,

Steve
Title: Re: linking obj file generated by VC cl with linker16
Post by: MichaelW on September 21, 2015, 04:11:38 PM
 Antique Software: Turbo C version 2.01 (http://edn.embarcadero.com/article/20841)

I seem to recall that one problem with version 2.01 was that printf could not handle floating-point values. Borland had a "patch" for a later version that I had, and it corrected the problem for 2.01, so perhaps it has been added to 2.01. If not, I think you can work around the problem by converting the value to a string with an available CRT function (gcvt, if I recall correctly).
Title: Re: linking obj file generated by VC cl with linker16
Post by: ggmasm32 on September 23, 2015, 04:33:28 AM
Quote from: dedndave on September 20, 2015, 01:59:04 PM
i seem to recall someone saying that turbo-c ver 2.0 was out there for free
to get microsoft, i think the last 16-bit version of their compiler was MSC v 6.0 or something

i had a same feelig here. Years ago, one of our Sr. Engineers gave me "helloworld" code complete with compilers and linkers which mixed asm and c code and generated exe file. Alas, I lost it. Big mistake. I do vaguely remember there was a tlink.exe within those files. That must be turbo-c stuff. I am gonna dig more on this one.
Title: Re: linking obj file generated by VC cl with linker16
Post by: ggmasm32 on November 09, 2015, 12:48:47 PM
ok, i got step closer to this now:

Here is what happened. I downgraded to vs2003 did not work, always seems to  generate 32-bit by VS.
So i downloaded and installed Turbo C4 version. Configured everything including all system variables.
But damn stdio.h include issue got in the way. Basically it can not find it. Since I am not using VS, it will not seek the stdio.h in the VS include location. Configured couple of system variables including LIB, CLASSPATH and PATH to point to Turbo C's include / lib location still not work.

Screw it, I said, I commented out the "include stdio.h" and anything that uses STDIO from the code from time being (so the c file is nothing useful but file with assignment to some variables  :t) and tried to generated executable.

Now everything just same but use masm to generate asm obj file, but for C, used tcc compiler and tlink from the turbo C.

As you can see below that persistent, annoying error regarding 4c error is gone:
However it still complained about 32-bit code in the assembly which i am looking at it now.

C:\GIT\MINIX.DEV\EXP>nmake asmc

Microsoft (R) Program Maintenance Utility Version 7.10.3077
Copyright (C) Microsoft Corporation.  All rights reserved.

        c:\masm32\bin\ml /c /Fl /Zi /Zd asmfile.asm
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

Assembling: asmfile.asm
        tcc /c cfile.c
Turbo C++ Version 3.00 Copyright (c) 1992 Borland International
cfile.c:
Warning cfile.c 19: Function should return a value in function main
Warning cfile.c 19: 'b' is assigned a value that is never used in function main
Warning cfile.c 19: 'a' is assigned a value that is never used in function main

        Available memory 4165820
        tlink.exe asmfile.obj cfile.obj,,,,,
Turbo Link  Version 5.0 Copyright (c) 1992 Borland International
Fatal: 32-bit record encountered in module ASMFILE.ASM
NMAKE : fatal error U1077: 'tlink.exe' : return code '0x2'
Stop.

C:\GIT\MINIX.DEV\EXP>

C:\GIT\MINIX.DEV\EXP>dir *.obj
Volume in drive C has no label.
Volume Serial Number is FC76-C34F

Directory of C:\git\minix.dev\exp

11/09/2015  05:52 PM             2,083 asmfile.obj
11/09/2015  05:52 PM               236 CFILE.OBJ
               2 File(s)          2,319 bytes
               0 Dir(s)  43,573,825,536 bytes free




When comparing the asm and C object files both starts with the 80 not 4c that tells something is hopeful

C:
80 09 20 07 63 66 69 6c 65 2e 63 dc 88 1f 20 20 20 1b 54 43 38 36 20 42 6f 72 6c 61 6e 64 20 54 

asm:
80 0d 20 0b 61 73 6d 66 69 6c 65 2e 61 73 6d 18 96 20 20 20 04 44 41 54 41 03 53 54 41 04 43 4f 

Title: Re: linking obj file generated by VC cl with linker16
Post by: dedndave on November 09, 2015, 04:32:32 PM
it looks like the data segment has a slightly different offset
80 09 20 is probably MOV AX,2009h
80 0d 20 is probably MOV AX,200Dh

also, some instructions may be coded more than one way

someplace, you have to tell turbo C that you want 16-bit segments
i'm not that familiar with it - played with it a little in the 80's, is all
Title: Re: linking obj file generated by VC cl with linker16
Post by: TouEnMasm on November 09, 2015, 06:17:21 PM
If both c and asm are in 32 bits try to use the masm32 linker instead of
the tlink out of date.
If it failed download  an old version of vc to avoid compatibilty problems.
Title: Re: linking obj file generated by VC cl with linker16
Post by: ggmasm32 on November 09, 2015, 08:17:38 PM
ok, now i solved majority of the problems presented in the last posts:
include issue: it is very funny, there is -Ixxx switch to tell compiler to specify the include path. But somehow by default, turboc4 was installed in c:\turboc4\tc folder and no matter how I tell the compiler to include header in c:\turboc4\tc\include location it wont. Later i found this turbo.cfg file in there, there were two switches, one of them was -IC:\tc\include. I thought what the heck and copied the tc folder to root and bamn! it worked. That was hell of a bad design if it can not include from arbitrary location.

Secondly, had to supply -3 switch to linker and 32-bit code complaint is gone. Now I can build asm and c mixed executable in two ways: either main is in C or asm.
Here is the partial makefile:


CC=cl
ASM=c:\masm32\bin\ml
TCC=tcc
TLINK=tlink.exe
LINK32=link.exe
LINK16=link16.exe

LINK=link16
CFLAGS=/c /W3 /Fm
AFLAGS=/c /Fl /Zi /Zd
TCFLAGS=-c -IC:\TC\INCLUDE"
TFLAGS=/3


# ====================================
#mix of ASM and C code goes here
#casm: c code has program entry: main()
#asmc: asm code has program entry: main()

ASMC: $(ASMC).exe stat

$(ASMC).exe: $(ASMFILE).obj $(LIBC1TCC).obj
    $(TLINK) $(TFLAGS) $(ASMFILE).obj $(LIBC1TCC).obj,$(ASMC).exe,,,,

#$(ASMFILE).obj: $(ASMFILE).asm
#    $(ASM) $(AFLAGS) $(ASMFILE).asm

$(LIBC1TCC).obj: $(LIBC1).c
    $(TCC) $(TCFLAGS) /o$(LIBC1TCC).obj $(LIBC1).c

CASM: $(CASM).exe

$(CASM).exe: $(ASMFILE1).obj $(TCC_CFILE).obj stat
    $(TLINK) $(TFLAGS) $(ASMFILE1).obj $(TCC_CFILE).obj,$(CASM).exe,,,,

$(ASMFILE1).obj: $(ASMFILE1).asm
    $(ASM) $(AFLAGS) $(ASMFILE1).asm

$(TCC_CFILE).obj: $(CFILE).c
    $(TCC) $(TCFLAGS) /o$(TCC_CFILE) $(CFILE).c


For the next step, I will work to enhance the code such that, the function declared in C is called from asm and vice versa and will update the here.
Once that part is dealt with I can move on to my main project. Thanks all!
Title: Re: linking obj file generated by VC cl with linker16
Post by: TWell on November 09, 2015, 08:34:36 PM
Quoteasm:
80 0d 20 0b 61 73 6d 66 69 6c 65 2e 61 73 6d 18 96 20 20 20 04 44 41 54 41 03 53 54 41 04 43 4f
Generated by HexEdit
test.obj

00000000  80 0d 20 0b 61 73 6d 66 69 6c 65 2e 61 73 6d 18  €. .asmfile.asm.
00000010  96 20 20 20 04 44 41 54 41 03 53 54 41 04 43 4f  –   .DATA.STA.CO
Title: Re: linking obj file generated by VC cl with linker16
Post by: ggmasm32 on November 10, 2015, 04:41:01 AM
there you go :)
Title: Re: linking obj file generated by VC cl with linker16
Post by: ggmasm32 on November 10, 2015, 08:32:40 PM
Alright last part, calling C function from asm, finally got it worked after lot of trouble, here is how it got it working:
it was one hell of a journey to destination.

Step1. Calling C without parameter and printing out the return value

I focused today on asm with main() entry point in asm plus C code, which means once final binary is executed, it starts from main declared in asm.

So, I attempted calling C function from asm and print out and return values.
Following wiki has fairly good reference for this purpose:
https://en.wikipedia.org/wiki/X86_calling_conventions

This worked provided following conditions are met:
- functions in C, once compiled with turbo C TCC, I can see it adds _ underscore to function name when looking inside object file. So function name was <fcnName>, then the corresponding obj file contains _<fcnName>. Therefore when calling from asm code, you call as

call _<fcnName>

Return values from the function will be saved in eax.

So with following C and asm snipped of code will print out the abcd when asm file calls M_PRINTWORD ax:

C file:

int extFcnC()
{
    return 0xabcd;
}


asm file:

...
    M_PRINTF "\ncalling extFncC..."
    call    _extFcnC
    M_PRINTWORD ax
...


M_PRINTWORD is a macro which prints out the parameter. Since C function return 0xabcd and return is saved in eax, then printing ax results in abcd

Here is the resulting output:

C:\git\minix.dev\exp>asmc
...
calling extFncC...ABCD
C:\git\minix.dev\exp>

Step2. Calling C WITH parameter and printing out the return value

For the next step, I modified the C code so that, it will accept 3 parameters and will do various arithmetic operation and based on the returned result, figure out the calling convention:

C file:
int extFcnC(int a, int b, int c)
{
    int ret;
    ret = a+b;
    return ret;
}


Asm file:


    M_PRINTF "\ncalling extFncC..."
    push    ebp
    mov     ebp, esp
    push    10h
    push    20h
    push    80h
    call    _extFcnC
    add     esp, 12
    M_PRINTDWORD eax


Here are serise of variation in arithmetic in C (bold blue above code changed) and returned results are logged

1)

    int ret;
    ret = a+b;
    return ret;


eax was 080h. in this case, it printed out the third parameter pushed. It is unclear how it got?

2)
    return a;
   
eax was 00h, in this case, a is not what it was supposed to be.

3)
    return b;
eax was 080h, which means, it was third parameter.

At this point, I came to conclusion easily that following two variant resulted in same:
1) return b
2) return (a+b)
But we know return a was 0, which means push 80h was translated into parameter b.

4)
    return c;


eax was 020h, which means, it was second parameter.

So using the C calling convention in the wiki above, it almost worked but not quite.
What I hoped was, series of pushes below will be assigned to parameter a, b and c respectively.
    push    10h
    push    20h
    push    80h



Instead, I got following assignments:
push 10h -> nothing
push 20h -> parameter C
push 80h -> parameter B   

So whole thing was off by word. I suspected this might have something to do with "near and far"-ness of the call

Conclusion:
This anomaly was caused by NEAR and FAR ness of the call. I found using the GRDB because the old-styled dos debugger was not able to correctly de-assemble some of the code correctly.

So here is what happens:
just before calling the extFcnC C function, I inspected the stack an BP, in another words, after pushing 10, 20 and 80 onto stack:
value were:
SP=F8 pointing to mem address: 80 00 20 00 10 00
BP=FE was point right past 80 00 20 00 10 00.


Now once I step through i discover following:
one step ahead and you are in C function.
At that point, the SP changed to F4 from F8. That means CS:IP must have been pushed meaning it is FARCALL.
after that it pushes BP (SS=F2) and mov bp, sp (SS=BP=F2) and
in preparation of returning the variable in case of A: it was doing
mov ax, bp[4]

that would be bp[4] = F6. But what is at F6? Lets see what the BP and SP both pointing at:
BP/SP = F2 -> BP CS IP 0080 0020 0010
You can see bp[4] is just a IP content. In other words, it fell 2 byte short of reaching the 0080 correct value.

By the same token
return b and c will return bp[6] and bp[8] respectively and in that instance will return 0080 0020 which is consistent with what I observed.

So here is the solution:
First of all, I have tried lot of different varieties to make it work but compiler refuse to compile it right. It is all off by a word or so and can not zero in into the right parameters:

- make extFncC as -> int _far extFncC() and declared it as EXTERNDEF:extFncC:far
Once done this way, both asm and C compiler will treat as a far call and far function and will push CS:IP onto stack but during the disassembly it was obvious compiler did it in such a way it pushes extra word 00 00 onto stack along with each of IP and CS so it looks as follows:
19 00 00 00 79 15 00 00 80 00 20 00 10 00

19 00 - IP[08] AND 00 00 SOME EXTRA 0-S I DONT GET IT
79 15 - CS AND 00 00 ANOTHER WORD OF EXTRA 0-S I DONT GET.

After lot of haggling around, made the "dirty" far call macro which is my last fool proof top gun  :eusa_dance: :eusa_dance: :eusa_dance::
(it was not my idea but i just remembered the way one of our top sr. engineers did a decade ago, and i am just blessed to have it recalled)

In this instance, declare C as far:
int _far extFcnC(int a, int b, int c)

Declare it in asm as far too:
externdef _extFcnC:FAR

But when calling use a macro:

M_FARCALL MACRO pFncAddr
    local   retAddr
    push    cs
    push    retAddr
    jmp     pFncAddr
retAddr:
    ENDM


and now make a dirty far call:

;   call    _extFcnC            ; go away you b***!
    M_FARCALL _extFcnC                        ; hello there???

What it does is as it push CS and IP[08] only with no extra 0 words and makes a JUMP to function address. Since IP can not be pushed onto stack directly, place a label after JMP to Function and that would be your return address pushed onto stack. After that it worked and each of A, B and C parameters were returned correctly from the C function.
Title: Re: linking obj file generated by VC cl with linker16
Post by: ggmasm32 on November 13, 2015, 05:47:47 AM
 AA