I am trying to recreate the C source code from some 16bit DOS 8086 assembly generated by the MS C 5.0 compiler. I've hit a wall with this far call instruction.
0000008D 9A2F0CB506 call 0x6b5:0xc2f
The MZ executable has a relocation entry on the 0x6b5 value which is actually the data segment. Looking at the data around the 0xc2f offset in IDA, this looks like an array of trampoline-like function stubs consisting of a 0xea
far jump opcode followed by a null segment and offset that are patched at runtime:
dataSeg:0C2F sub_0C2F proc far
dataSeg:0C2F jmp far ptr loc_5C30
dataSeg:0C2F sub_0C2F endp
dataSeg:0C34 sub_0C34 proc far
dataSeg:0C34 jmp far ptr loc_5C35
dataSeg:0C34 sub_0C34 endp
dataSeg:0C39 sub_0C39 proc far
dataSeg:0C39 jmp far ptr loc_5C3A
dataSeg:0C39 sub_0C39 endp
Now I'm trying to write C code that would generate the identical assembly:
uint8 test[] = { 0xea, 0x12, 0x34, 0x56, 0x78 };
typedef void(far *func)(void);
int main(void) {
// cast data segment address to a function pointer and call
((func)test)();
}
However, the compiler generates this instead:
00000080 B84200 mov ax,0x42 ; the offset of 'test' in the data segment
00000083 8CDA mov dx,ds
00000085 8956F0 mov [bp-0x10],dx ; put segment and offset on the stack
00000088 8946EE mov [bp-0x12],ax
0000008B FF5EEE call far [bp-0x12] ; call through temporary pointer placed on the stack
I've tried using a different memory model (medium/compact), changed the array declaration to static and far, still no luck.
One thing I noticed (thanks user @the busybee) was that when using the medium model (multiple code segments), functions from a different translation unit (.c file) are placed in a different code segment, and called through a literal segment:offset far pointer call like I need, but there doesn't seem to be a way to force those functions into the data segment, or conform to the 5-byte layout (jmp opcode + 4 bytes address) from C.
Another thing I tried next was to put the uint8
buffer in a different translation unit, but an attempt to cast its address to a far function pointer still results in a call via a temporary far pointer on the stack.
What other C code could potentially generate a far call into the data segment with an immediate relocated segment and offset like the original?
extern void far test(void);
in one C file anduint8 test[] = { ... };
in the other C file? Using Microsoft Quick C for DOS, this approach worked.