The Linker, and a Flags Example

CS 301 Lecture, Dr. Lawlor

So there's a new NetRun feature available today, "Link With:". The deal is you just type in the name of some saved routine into the "Link With:" box, and that routine gets linked into the routine you're running. For now, you can only link two routines together, but if you call the linker yourself outside NetRun, you can link as many separate files as you want.

Try it out on my tiny multi-precision arithmetic class by first grabbing this assembly code, then run it with this C++ code.

Saving and Restoring Flags

Note in the above example multi-precision assembly code, the code would be a lot cleaner if written as a loop.  But unfortunately, the "cmp" instruction (to test if we're done with the loop) overwrites all the flags, including the carry flag.  So we actually have to save and restore the status flags every time through the loop, using the LAHF (load ah with the flags) and SAHF (store ah into the flags) instructions:
;void assembly_add_mp(dest,src1,src2)
;Do a multi-precision addition of these two source numbers into dest.
; Stack:
; src2
; src1
; dest
; return address
global assembly_add_mp ; <- makes subroutine visible from outside
assembly_add_mp:
mov esi,[esp+4]; dest, destination array
mov edi,[esp+8]; src1
mov edx,[esp+12]; src2
mov ecx,1; i, Loop counter

mov ax,0
push ax ; Zero out status flags
START_O_LOOP:
; Restore status flags
pop ax
SAHF

mov eax,[edi+4*ecx]; src1[i]
adc eax,[edx+4*ecx]; src2[i]
mov [esi+4*ecx],eax; dest[i]
; Save cursed flags
LAHF
push ax

inc ecx; i++
cmp ecx,[edi+0]; i<=length of arrays
jle START_O_LOOP

pop ax; Clean up stack

ret
(executable NetRun link)

Name Mangling and extern "C"

C++ "mangles" the linker names of its functions to include the data types of the function arguments. This is good, because it lets you overload function names; but it's bad, because plain C and assembly don't do anything special to the linker names of functions.

In C or assembly, a function "foo" shows up as just plain "foo" in the linker. In C++, a function foo shows up as "foo()" or "foo(int,double,void *)". (Check out the disassembly to be sure how your linker names are coming out.)

So if you call C or assembly code from C++, you have to turn off C++'s name mangling by declaring the C or assembly routines 'extern "C"', like this:

extern "C" void some_assembly_routine(int param1,char *param2);
or wrapped in curly braces like this:
extern "C" {
void one_assembly_routine(int x);
void another_assembly_routine(char c);
}

In fact, it's common to provide a "magic" header file for C code that automatically provides 'extern "C"' prototypes for C++, but just works normally in plain C:

#ifdef __cplusplus /* only defined in C++ code */
extern "C" {
#endif
void one_assembly_routine(int x);
void another_assembly_routine(char c);
#ifdef __cplusplus /* only defined in C++ code */
}
#endif
Definitely try these things out yourself:

Plain C bar routine:
int bar(int i,int j) {
printf("bar(%d,%d)\n",i,j);
return i;
}
(executable NetRun link)

C++ foo routine that calls bar:
extern "C" int bar(int i,int j);
int foo(void)
{
return bar(2,3);
}
(executable NetRun link)

Try:
Code written in
With name
Has linker name
C++
int bar(int a,int b)
bar(int,int)    <- But "mangled" to be alphanumeric...
C++
extern "C" int bar(int a,int b) bar
C
int bar(int a,int b)
bar
Assembly
global bar
bar:
bar
Fortran
SUBROUTINE bar()
bar_, BAR, BAR_, bar__, or some such.  Disassemble to be sure...

Bottom line: to call C or assembly from C++, or to call C++ from assembly or C, add 'extern "C"' to the C++ code.