Linking Assembly and C/C++

CS 301: Assembly Language Programming Lecture, Dr. Lawlor

Here's how you write an entire function in assembly.  The "global bar" keyword in assembly tells the assembler to make the function name "bar" visible from outside the file.

global bar
bar:
add rdi,1000
mov rax,rdi
ret

(Try this in NetRun now!)

The "Link With:" box (under "Options") tells NetRun to link together two different projects, in this case one in C++ and the other in assembly.  This C++ code calls the assembly here.

extern "C" int bar(int param);

int foo(void) {
return bar(6);
}

(Try this in NetRun now!)

You can call C++ code from assembly almost as easily, by making the C++ code extern "C", using "extern someName" in assembly, and then call the function normally--this is exactly the same way you call plain C code from C++ or vice versa.  (If you don't do extern "C" on the C++ side, it's difficult to find the function's name, due to C++ name mangling.  Name mangling also makes it hard to call C++ operators and methods from C or Assembly, so stick with C++ functions.)


Calling a function bar written in...

C++
C
Assembly
Called from C++
long bar(long) { ... }
long bar(long);
long bar(long) { ... }
e
xtern "C" long bar(long);

global bar
extern "C" long bar(long);
Called from C
extern "C" long bar(long) { ... }
extern long bar(long);
long bar(long) { ... }
extern long bar(long);
global bar
extern long bar(long);
Called from Assembly
extern "C" long bar(long) { ... }
extern bar
long bar(long) { ... }
extern bar
global bar
extern bar


Linking Assembly and C++ at the Command Line

The most portable way to include some assembly functions in your code is to compile the assembly in a separate file, then link it with the C++.  For example, in a file name "foo.S":

section .text
global _foo
_foo:
mov eax,7
ret

(Note the weird underscore in front of the function name--the compiler adds these on Windows and OS X, but not Linux.)

You'd assemble this into "foo.obj" on windows with this command line:

    nasm -f win32 foo.S

Then in a file named "main.cpp", we call foo with an extern "C" prototype:

#include <iostream>
extern "C" int foo(void);

int main() {
std::cout<<"Foo returns "<<foo()<<"\n";
return 0;
}

We compile the C++ and link it to the assembly using the Microsoft Visual C++ compiler like this:

    cl -EHsc main.cpp foo.obj

(You may have to run "vc_vars.bat" to get "cl" into your PATH.)

We now have a functioning C++/Assembly executable!  The same exact command-line trick works on Linux (nasm -f elf32   or   nasm -f elf64) or OS X (nasm -f macho32   or   nasm -f macho64) as long as you're compiling with g++ or gcc.  If you don't like the command line, and few people do (except me!), you can hide the NASM command line inside Visual C++ as I explain here.