Want to write a single program that uses C++ for high-level logic, C for portability, and assembly for speed? This video shows exactly how to do it.
We build a complete hybrid program from scratch:
- driver.cpp – contains main(), prints intro, calls assembly
- c.c – simple printf() function
- cpp.cpp – C++ function (with extern “C” to disable name mangling)
- hello.asm – assembly module that prints messages, calls C and C++ functions
You’ll see:
- Why hybrid programs matter
- How source files → object files → final executable
- The role of the driver and why main() lives once
- Name mangling in C++ and how extern “C” fixes interop
- Global / extern in assembly to expose and call functions
- A clean Makefile that compiles each module separately
- Live build and execution with output explained line-by-line
By the end you’ll know how to call across languages safely, pass basic args via registers, and structure projects for fast incremental builds.
No prior assembly required – just basic C/C++ familiarity. Code is copy-paste ready.
Timestamps in comments. Source files linked below.
Like & subscribe for more systems programming!
What is a Hybrid Program 00:00:00
Why Use Hybrid Programs 00:00:21
Program Modules Overview 00:01:10
Compilation to Object Files 00:02:23
Makefile Introduction 00:04:26
Driver Module Setup 00:06:43
Name Mangling Explained 00:07:18
C++ Module with extern C 00:07:43
Driver Main Function 00:10:12
C Module Implementation 00:11:52
Assembly Module Setup 00:12:41
Assembly Data Section 00:13:03
Assembly Text Section 00:14:18
Global and External Functions 00:14:35
Assembly System Calls 00:16:20
Calling C from Assembly 00:18:58
Calling C++ from Assembly 00:20:32
Program Execution Flow 00:21:05
Running the Program 00:23:59
Passing Arguments Between Modules 00:26:23
Hybrid Program Summary 00:29:11
Thanks for watching!
Find us on other social media here:
- https://www.NeuralLantern.com/social
Please help support us!
- Subscribing + Sharing on Social Media
- Leaving a comment or suggestion
- Subscribing to our Blog
- Watching the main “pinned” video of this channel for offers and extras
Hello there. Let’s talk about hybrid programs.
What’s a hybrid program?
A hybrid program is basically a program where you have multiple modules,
many written in different languages, all compiled into one executable program.
Why would you want to make a hybrid program? Well, you know,
maybe you have C++ as one of the modules that you’re creating and C++ is a high
And C++ is a high level language, so it makes it easier to write big, robust programs with GUIs and like big design, big fancy design patterns and things.
But maybe at some point you want to interface with the C program.
So it might make sense to, you know, kind of jump into some C code.
Maybe also you want to write a little module in assembly because maybe one part of your code needs to be really, really fast and efficient in some way.
the code compiles down to machine code.
Assembly is just, you know, a level up from machine code.
So let’s see, I’m just gonna write a quick program
to demonstrate that you can do this
and you can have these modules call each other.
First off, I should probably draw a little bit
about what I’m even talking about with multiple modules.
So I’m gonna do my little annotator here
and yeah, there we go.
Basically imagine that you have a C++ module
and we’ll call it the driver.
and we’ll call it the driver and I’ll say like driver.cpp. Okay I think I’m
drawing this backwards. I’m gonna draw this down here. Driver.cpp.
Excuse my penmanship I don’t have a smoother setup on this thing. So we have
driver.cpp that’s one source code. Maybe you have like another module, another
just some C code. The driver, usually the way I like to refer to it, and one of my favorite,
you know, assembly books refers to it is the driver is where the main function is so that
it’s sort of like the entry point of your program. But then maybe we’ll have another module that has
some other kind of CPP code in there. I’m just going to call this file cpp.cpp. And maybe we
have another module that’s written in assembly. I’m going to call this hello.asm. Okay, so just
Okay, so just several source code files.
We will compile them each to an object file.
Sometimes, I think, with new programmers,
they think that you just compile a source code directly to the executable.
We’re not going to do that in this case.
We’re going to get a little bit fancier.
Doing it this way, by the way, will increase your compiling efficiency
when you start getting used to make files.
I don’t know if you’re already using those, but it’s a good idea.
So we’ll write a script to compile or make file to compile
or some kind of a build system to compile.
We’ll compile the CPP driver to just a file called
driver.o, O for object file.
So O for object file depends on driver.cpp.
We’ll have another file called c.o,
so the object file c.o depends on c.c and so forth.
So I’m just gonna go cpp.o, circle it,
and then down here we’ll do hello.o.
Notice how all of these modules, regardless of the language they are written in, compile
down to an object file.
That’s really what it is.
The higher level languages are just an illusion for us humans.
So then at the very top, we want our actual executable file.
We’ll call it main because that’s kind of a standard thing sometimes.
So it’s like our main, we’ll have a file named main that we can execute.
is going to be the main function.
Spoiler, it’s going to be inside the driver.
Remember, the main function, if you’re using GCC libraries,
has to appear in exactly one place, one module in your program.
It should appear one time and one time alone, not zero or more than one.
So the main executable depends on the driver object file,
the C object file, and the C++ object file,
and the assembly hello object file.
Looks complicated at first until you stare at it a while,
complicated at first until you stare at it a while and then you just realize,
oh, every source code is going to compile to its own object file.
And then after we have all the object files,
we’re going to have our executable just sort of linked together at the final
linking stage. So with that in mind,
I’m just going to copy paste a make file that I already wrote.
It’s like my pre solution because this is not a make file video.
And I just want you to know just real fast what it looks like.
All I’m doing is I’m going to make some variables, uh,
for my C++ compiler flags and my C compiler flags and my Yasm assembler flags.
I’m really not doing anything fancy.
I’m just sort of setting up some variables so I don’t have to type.
The newer Ubuntu stuffs need a no executable stack flag and a no pi flag.
So here we’re just like making variables for linking.
My executable’s name is going to be main over here.
I personally like to make make files that default to a cute help menu.
help menu. I know most people just default to building, but, uh, you know, for educational
purposes, I think this is a little more fun, a little easier. So this way later I can just type
make menu or make build or make run or make clean or whatever I want to do. And, um,
I’m going to make a target called run, which is what we’re going to be using. And then a target
called build, which just only builds everything. The run target obviously depends on build so that
to run it and and if you don’t know make files at this point it’s okay this is not a make file video
i’m going to make some make file videos in the future
then my binary the main program is just going to depend on all these object files notice how it
depends on the driver the hello the c plus plus and the c object files so really uh at the linking
stage we’re just going to use g plus plus to just sort of link all the object files together
Then we have a little section to compile each source code file.
So in order to make driver.o, I’m going to compile,
I’m going to depend on the driver.cpp.
And then when it comes to actually compiling, compiling it,
I’m just going to type the commands to compile it here.
Don’t worry about these crazy variables.
I’m going to make more videos in the future.
And then each module gets compiled in its own way.
Then I just have a little target at the bottom called clean because I like to be
able to just clean my build area of object files.
I don’t know about you, but I like a clean build area.
I’m a clean build area guy.
I don’t like a dirty build area.
So that was the make file.
Again, this is not a make file video,
so don’t worry too much about it right now.
The next thing we should do is make a driver.
Again, I’m gonna copy paste from my solution here
because the driver should be easy.
This is really, this video is mostly gonna be
for people who have never done assembly before.
So I’m just gonna assume you know a little bit
about C++ and C already.
So I’m going to say touch, let’s see, driver.cpp so that I can get the driver file in there.
And I’m going to go nano driver, probably put it down in the first place.
Then I’m just going to paste my solution.
Oops, you can tell I copy pasted this from my IDE because I have the extra tabs in there, which I love.
One thing to keep in mind is that, I guess like one of the first rules that I want to tell you for hybrid programs
is that assembly doesn’t understand something called name mangling.
mangling. So what is name mangling? Name mangling is basically when you have a,
oh, this is not going to get called. So this is maybe not a great idea to show you name
mangling. Let me, let me start on one of the other ones real fast too. Let’s do
the C++ module. Okay. So I’m going to go nano CPP dot CPP. I know it’s a silly name. I know it.
So CPP dot CPP, this is just a simple C++ program, which is meant to be called by
by the assembly module just to show you that we can.
And so we just have a little function here,
void, you know, the CPP function.
So it’s just a function that doesn’t really return anything.
Inside of it, I’m just going to print,
hello, this is the C++ function, no problem.
Notice at the top,
this is the first important thing that you should understand.
In C++, we have overloads, right?
You can have the same function name,
but with different argument lists.
And so that gets resolved under the hood
by the name actually kind of changing
based on what the argument signature is,
what the prototype signature is.
So that’s great and all,
but in assembly, at least at this level,
there’s no way to really differentiate between overloads.
In assembly, we’ll just call a name of some function.
But if we try to call a name,
let’s say of just the CPP function,
if we say call the CPP function in assembly,
it’s not going to work
because the actual function has not been compiled to have that name.
It’s been compiled to have the CPP function
and a bunch of extra symbols indicating what the argument list is.
This is great for C++ programs, but it’s poor.
It’s bad for other modules to be able to call the C++ function
because they won’t really know what the actual name is
after the mangling has been done.
So what you want to do is make a little block called Xtern C,
just like this.
You say extern and then C in quotes and then do a scope with braces.
And inside of that, just put the prototypes of any function that you want to be available to other modules.
So, for example, we want the CPP function to be available to assembly modules and C modules, not just other C++ modules.
So, I’m just going to put the prototype of it right there.
You can put as many prototypes as you want.
I think they should come from the same source file.
different source file where you want something to be available elsewhere,
you would put an extern C block for its prototype.
This disables name mangling,
which means the compiled name of the CPP function will actually be the CPP
function. So it’s pretty good. This will make it available to assembly.
So now that I’ve kind of explained that I can maybe close this and go back to
what I was doing before. So this is the main driver program.
We don’t need to engage in a dis,
we don’t need to disable name mangling for the main function because no one is going to call main
except the GCC libraries or like the OS. But when the C++ module calls other modules, it needs to
be able to call the right name. And remember in C++, there’s name mangling enabled by default.
So if I tell it, let’s say on this line right here, or you can do alt N to get line numbers on nano.
we want to call on the hello module will C++ by default will assume that hello has name
mangling applied to it, which means it will actually try to call some name that is totally
different based on the prototype signature, the incoming arguments and things like that.
But so we want to disable that because we wanted to just call the actual name because
hello is going to be a function that we make inside of assembly.
So it’s not going to be name mangled.
So that’s why up here on line seven, we enter its signature.
Enter its signature. Oh, actually this is from a different version of something that I was doing. I’m not going to return anything from hello
We’ll do name a demangling with our extern C block just void hello
And now when we call hello, it’s actually going to call a function called hello without any other
mangling same thing for the C S E A
Oh
That was from a different version. I wrote we’re not going to be calling C in this module. Don’t worry
We’re going to keep it simple
So pretty much we’re going to print hello and then we’re going to print,
then we’re going to call the hello and then we’re going to say goodbye.
And then that’s going to be the end of the program.
So let me close out of that and just double check what I have written right now.
So we’ve got CPP and okay, we need to do C, nano C dot C.
Okay, so let me get my solution real fast here.
It’s a simple program.
Literally, it’s just a C program that prints something.
Remember in C++, we’ve got the IO stream, but we don’t have that in C.
that in C. So I’m just going to use printf. Oh, by the way, this is a C function. Hi.
So done with that. Pretty easy. Shoot. You know what? I need to start learning how to do
clear and LSA. So I can just go up, up, up. So C++, C and the driver
and the make file, which is huge. I just realized that. And now I guess we can start
or hello function.
Okay.
So maybe I want to write this in the Genie program.
So I’m going to, well, maybe I’ll first, I’ll say touch,
and I’ll say hello.assembly.
And then now that it’s there, I can try to open it up.
Let’s see.
Hello.assembly.
Okay, so now let’s write an assembly program.
I’m not going to spend too much time talking about this,
but I’ll say like data section here,
and we’ll say section.data.
We’ll say section.data and then we’ll say system calls.
This is not a system calls video.
I’m just gonna do this very quickly without explaining.
System write is gonna be code one, I guess.
And then we’re gonna do file descriptors, one for stdout.
Whoops, stdout, it’s gonna be equal to one.
This is not a file descriptors video.
I’ll put FDs, I guess.
And then we’ll make some C strings.
C strings, you know, just character arrays, basically.
I’ll say, well, you know what?
I don’t want to type this all out
because this is not a lesson on variables.
I’m just going to copy paste my existing solution here.
And, you know, long story short,
I’m going to try to make this a pretty program
that prints hello and goodbye and things.
And so we just have like some variables
that sort of just you know old strings and hold the strings length and we’re
gonna do one that calls a new line and so forth actually that’ll be a different
video but I’ll leave it in there for now so now let’s make the text section
remember the text section in Yasm is just for the actual code of your program
and then we need to set up some external symbols. So right now, at least for the purposes of this
source code, this source code is inside of the hello module. So you can imagine we’re inside of
hello.o. All the o’s get linked together in the executable, but between themselves, there’s kind
of a little bit of like a border situation happening. There’s like some separation. So
are going to be available to the other modules,
even if they’re part of the same executable.
It’s kind of convenient because you would like sometimes
to be able to write tons and tons of functions
that are only available inside of your assembly module,
and they might not be available to other modules,
and therefore you don’t want to pollute the namespace,
I guess, of the global namespace of the executable.
So by default, if I make a function called hello,
how you make a function, just a quick recap here.
This is not a functions video.
make a symbol with the name of the function you want and then you just put a ret after it and then
you hope that whoever got you there is going to use the call instruction but anyway for now
we’re just going to say this is our hello function okay i cannot call the hello function now from
another module i have to make it visible to the other modules with a special keyword called global
our program the hello function will be available to the other modules but not anything else that
I type without marking it as global so let’s go ahead and do let’s do a welcome message and this
is not a system call a video maybe I should just copy paste this so just looking at it this is a
going to quickly you know breeze through what we’re doing in case you’re interested but you
don’t have to care too much about this right now we’re going to tell rax what system call we want
to make the code for that is what i put up in system right which is just code one means let’s
write to a file we’ll give it the file handle uh that’s going to be the variable we made called fd
std out which means we’re going to be giving it the file handle of one which means we’re telling
to print to standard output because file handle one is always standard output.
And then we just give it the a pointer to the first character of the message
string that we made and then tell it how long that message is. Then we do system call this should
actually um this this should actually print a string. Okay so then uh let’s see down here I’m
going to make a function called crlf because I’m lazy and I don’t like constantly adding extra stuff
constantly adding extra stuff to the string so that there will be like a new line line feed
situation so i usually like to make a little function somewhere called crlf carriage return
line feed is what it stands for and it’s the same thing it’s just like load up the system call
registers so that it knows i want to print the string which is just a line feed
carriage return line feed and then actually call system call and then return to the caller so this
back up here let’s see crlf i guess i am going to be doing it it’s just a string with the the
slash r slash n or the carriage return line feed characters in ascii those are 13 comma 10 so i can
put that there instead of a literal quoted string okay so we got that and then that means after the
welcome message i can just like call crlf in order to have uh the cursor jump down to the next uh
after that string is notice how I don’t have any line feeds after my strings that’s kind of why I
like to do crlf and it lets me just call crlf a bunch of times if I want to just like space things
out temporarily okay so we’ve got the hello function and now we’re going to print another
message here I’ll just explain this real fast so I’m going to print a message about calling a c
So same thing, we’re just going to tell the system call service that we want to print a string and it’s going to be our string here that says, you know, we’re about to print this.
We’re about to, what do we say?
We’re about to call the C function and then later we’re going to call the C++ function.
So we’re just going to print a message announcing and do CRLF.
So that’s really nothing new at all.
And then down here we actually call the C function, which is not going to be in this module.
It’s going to be in the C module, the other source code.
Somewhere in this program, we need to let our assembly module know
that the C function is available because it kind of goes both ways.
In assembly, I have to mark a function as global if I want other modules to be able
to call that function, but I also have to mark a function as external
if I want to be able to call some other modules function.
So we have to go back up to the top.
I mean, somewhere in the text section, I think would be fine.
But for me, I just like to put it at the top.
put it at the top at the top so I’ll say externals and then I’ll go extern
keyword and then just name the function that I intend to call this way when the
linker is trying to link all the objects together it doesn’t get upset because
you tried to call a C function which is not in the current module it’ll
understand that it’s gonna happen you know like it’s gonna call other modules
for that it usually gets upset right away and then while we’re at it let’s do
Let’s turn the CPP function because that’s going to come from the C++ module.
So now we have both of our external functions set up.
We’ve marked our global hello function so other modules can call us.
And then let’s see, oh, what did I do wrong?
I put the C message in the wrong spot.
I was inside of the CRLF function.
So I’m just going to copy paste that back up here.
Okay.
Then we’ll do the same thing for the C++ message,
or for the C++ stuff.
I will first print a C++ message
with these lines up here.
That’s just like a regular system call,
and then I’ll do a CRLF, so nothing new,
just printing something.
And then I will actually call the C++ function
called the CPP function.
Then I’ll say goodbye with another block of text.
Again, not super important or complicated,
so I’m just gonna paste it.
Just another string.
string. And then when we’re done, I’ll just say, you know, return, we’re done. So that means if
you notice how we don’t have like any symbols in here called underscore start, that means this is
not a pure assembly program. And we don’t even have a symbol inside of here that is called main.
So that means this hello ASM module is not going to be the entry point of our program. It’s just
going to expose a function called hello, which does some stuff. The entry point of our program
really, let’s see, where is it? Oh, gosh, I think I already closed it for some reason. It’s the
driver program, which we talked about. So remember the driver program, it’s got a main here. So that
means this is going to be what the GCC libraries or the operating system calls, however you want
to think of it. So this is the entry point of our program, it’ll just print something quickly,
then it’ll call the hello function, which means execution will jump in to the hello module. So
module and and also it would be really easy to apply this logic to have a c module call on an
assembly module and in fact you can use this logic to have every type of module call on any other i
mean just if you have a c++ function either being defined in the current module or being referenced
in another module just make sure that you have an extern c block and then if you’re inside of a c
and you’re going to call a non-name mingled module like C or assembly,
then just do this block here.
No problem.
What else can I really tell you?
The C module, we looked at that.
The C++, we looked at that.
Pretty easy.
Hello module.
I mean, I guess we’re kind of done already.
So what we should see now is a little intro here.
Hello, my name is whatever.
And then when we jump into the hello module,
we should see a little welcome message about being in the assembly module.
And then we should see a message about calling the C function.
And then the C function should print.
Let me open that up for you.
Let’s see.
The C function should print.
Just, oh, by the way, this is the C function.
And then when it returns at the very end, remember, even in a void function, there is
a return statement implicit at the end of scope.
So when it returns, it’ll come back down here.
And then the C++ message will get printed.
printed and then the C++ function will get called which is going to end up being
just it just prints you know another message this is the C++ function
then when it returns implicitly at the end of the function
then we get down to here where we just basically say goodbye and then return to the caller
the caller in this case is going to be the driver program because this whole time we were just inside
called. So then the driver will say goodbye and then it will return control to the GCC libraries
or the operating system ending the program. Let’s see if that works. Okay, clear. And so,
you know, the way I wreck my make files, I’d like to do a little menu. I type make and it’s like,
oh, the menu, make menu, make build, just in case, just to see what’s up. Okay, clear.
say, let’s do clear and make run because that’s really what we’re after.
Again, run will implicitly call build.
So do that.
And it seems to have succeeded.
It wasn’t actually very exciting.
Let me do a make clean so you can see it compile real fast.
We run it.
It’s compiling.
That was still kind of fast.
So up here is basically the make file, all these lines.
They’re just calling your normal compiler and assembler tools and linker tools.
linker tools and then here’s the driver like we said it prints the hello message
you know my name is whatever I love that name I have a strange name generator
that I found online that’s not my name you can call me that if you want to and
then we have the message that we’re going to call the hello module so now
we’ve called into the hello function inside of the assembly module assembly
module prints hello you know I’m inside of here now and then the assembly module
Now we’re going to call the C function and then the C function actually
executes and it is now printing with its print F function call that it did,
you know, by the way, this is a C function. Hi.
And then it returns to the caller.
Maybe I could have added another print statement if I felt like I wanted to
spend more time, but it returns to the caller, which is the hello function.
Then we get to the next part of the hello function where it’s like now calling
the C function as a print statement.
Then it actually does call the C function. The C function prints itself out.
prints itself out hello this is the C function and then the assembly module
says goodbye it says it’s finished and then it returns to the caller so finally
at the end of this print statement the there’s like that little return statement
at the bottom of the hello module that returns to the caller which was the
driver dot CPP program so then the driver prints the driver is now back in
the driver is now back in control. You can see that message behind the terminal here.
The driver is now back in control. Then it says goodbye. Then it returns zero to the operating
system and the whole program is done. So it’s important to understand that if you want to
pass arguments, right now I’m just going to say, you know, I’m going to make another video that
is more robust in the future about passing arguments back and forth between these. It’s
important to understand for now that you can pass arguments between all these different
between all these different module functions if you want.
Like if I wanted to give the.
If I wanted to give the hello function.
An incoming argument, let’s say like an integer or a character value or a pointer
or something like that, I could just load up appropriate registers.
Remember, we have argument registers in assembly.
I think I can never remember if RSI or RDI are the first one.
I’m going to say RDI is the first argument just off the top of my head.
I always look that up.
So if I wanted to call, let’s say give an integer argument to hello when I originally
called it, then from the driver, I literally would just have to type an integer argument
in there and then under the hood, the RDI register, assuming that I’m right about RDI
being the first argument, the RDI register would get loaded up with that value.
your assembly program all you’d have to do is do something with the RDI register
you know load it up save it do whatever it is that you think you’re gonna do so
that’s how you pass an integer or like a character or a pointer from C++ to
assembly it works the same exact way if you want to pass arguments when calling
from C to assembly and when if you want to pass integers from assembly to C or
or C++, it’s the same thing just backwards.
First, you load up RDI as the first argument register with whatever value you want to pass
to C or C++.
And then in the signature of the C++ function, you just expect, you know, a long or, you
know, whatever you were going to get and the register will already be loaded up.
Remember, the C++ is not really happening on the machine.
It gets compiled down to assembly.
So this source code that we’re looking at, it’s another illusion for our benefit.
benefit. It’s not happening. It’s getting assembled and then compiled down to machine code. And then
that is what’s actually happening on the machine. So when you write that you have an argument in a
C++ function signature prototype, you’re not really writing that at all. You’re just saying
that the A variable should get loaded from whatever the RDI register had.
Or if you previously loaded the RDI register and you want to call a C++ function, that’s
a C++ function that’s what it would expect the RDI register so it can work pretty seamlessly
just calling things back and forth um so yeah I guess that’s all I really had to say
this is the basics of writing a hybrid program thank you for watching I hope you learned a
little bit of stuff and had a little bit of fun see you in the next video
thanks for watching this video again from the bottom of my heart I really
appreciate it I do hope you did learn something and have some fun if you could
do me a please a small little favor could you please subscribe and follow
this channel or these videos or whatever it is you do on the current social media
website that you’re looking at right now it would really mean the world to me and
it’ll help make more videos and grow this community so we’ll be able to do
more videos longer videos better videos or just I’ll be able to keep making
videos in general. So please do me a kindness and subscribe. You know, sometimes I’m sleeping
in the middle of the night and I just wake up because I know somebody subscribed or followed.
It just wakes me up and I get filled with joy. That’s exactly what happens every single time.
So you could do it as a nice favor to me or you could troll me if you want to just wake me up in
the middle of the night, just subscribe and then I’ll just wake up. I promise that’s what’ll happen.
Also, if you look at the middle of the screen right now, you should see a QR code, which you
QR code which you can scan in order to go to the website which I think is also named somewhere at
the bottom of this video and it’ll take you to my main website where you can just kind of like see
all the videos I published and the services and tutorials and things that I offer and all that
good stuff and if you have a suggestion for clarifications or errata or just future videos
that you want to see please leave a comment or if you just want to say hey what’s up what’s going on
you know just send me a comment whatever i also wake up for those in the middle of the night i get
i wake up in a cold sweat and i’m like it would really it really mean the world to me i would
really appreciate it so again thank you so much for watching this video and um enjoy the cool music
as as i fade into the darkness which is coming for us all
Thank you.

