- 2 days ago
Your x86-64 assembly program looks correct, links with GCC, calls printf, and then crashes with a segfault. This video shows exactly why it happens and how to fix it.
We build a hybrid program using Yasm and GCC on Linux, print a message from C++, call an assembly function, and hit the crash. Then we fix it with a push and pop of RAX, move the fix into the function prologue and epilogue, and run experiments to prove it works.
No long lectures, just code, a real crash, and a real solution that stops the problem for good.
Introduction 00:00:00
Hybrid Programs 00:00:09
Stack Alignment Problem 00:00:33
GCC Stack Expectations 00:01:14
Makefile Overview 00:02:22
Driver Code 00:03:16
Assembly Module Setup 00:04:12
Calling printf 00:05:20
Program Crash Demo 00:07:17
Diagnosing Segfault 00:07:56
Fix with Push-Pop 00:09:08
Prologue Epilogue Fix 00:11:18
Experiments Confirm 00:12:54
Conclusion 00:13:32
Outro Thanks 00:13:52
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
We build a hybrid program using Yasm and GCC on Linux, print a message from C++, call an assembly function, and hit the crash. Then we fix it with a push and pop of RAX, move the fix into the function prologue and epilogue, and run experiments to prove it works.
No long lectures, just code, a real crash, and a real solution that stops the problem for good.
Introduction 00:00:00
Hybrid Programs 00:00:09
Stack Alignment Problem 00:00:33
GCC Stack Expectations 00:01:14
Makefile Overview 00:02:22
Driver Code 00:03:16
Assembly Module Setup 00:04:12
Calling printf 00:05:20
Program Crash Demo 00:07:17
Diagnosing Segfault 00:07:56
Fix with Push-Pop 00:09:08
Prologue Epilogue Fix 00:11:18
Experiments Confirm 00:12:54
Conclusion 00:13:32
Outro Thanks 00:13:52
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
Category
🤖
TechTranscript
00:00Hello there. Let's talk about stack alignment.
00:09When you're linking and running a program for x86-64 using a YASM assembler and also
00:15you have a hybrid program so you're using the GCC libraries in order to get some extra functions or
00:21extra functionality or just so you can have different types of source code in your program.
00:26Okay, so this video is not about how to make a hybrid program. Look for my other video
00:31for that topic. This video is only about solving a common problem which costs people
00:36tons of time called stack alignment. Imagine this, you're writing a program,
00:41a hybrid program, and every once in a while you call on a function within the GCC libraries or
00:47perhaps you call another function that you don't control or something that might in turn call on
00:54a GCC library and you can't figure out why but your program keeps crashing for no reason. Seemingly
01:00you've spent hours and hours debugging your program. It appears to be correct, but it still crashes.
01:06If that happens to you, there's a chance that you're suffering from stack alignment issues.
01:11So what do I mean by this? I mean basically the GCC libraries, they expect that your stack,
01:19which is the data structure you use, you know, when you're calling a function or returning from a
01:23function or you're making local variables or you're like pushing, you know, when you're pushing and
01:28popping stuff onto the stack. GCC expects that the stack is aligned to eight bytes.
01:38So that means basically every time you do a push or a pop, the stack kind of goes out of alignment
01:45and goes into alignment. So like I do a push, the stack goes out of alignment. I do another push,
01:50the stack goes into alignment. I pop later, it gets out of alignment. I pop again,
01:55it goes into alignment. Every time you do a function call, every time you modify the stack by,
02:00you know, a certain number of bytes, it could potentially be going out of alignment.
02:04I think it stays into the same alignment if you push twice, as far as I recall, or if you jump quickly
02:11twice. But the point is, this is kind of a precarious thing. And maybe sometimes you're
02:15not sure. Hmm. Am I suffering from stack alignment? Well, here's how to check and make sure that it's
02:21not happening. So first off, I just want to show you my make file real fast. This is not a make file
02:26video. So I'm going to just really, I'm going to very quickly go through it. This is a hybrid program.
02:30So I just have some variables at the top for C++ compilation and some variables over here for
02:36assembly in Yasm. And then I'm going to link using G++. Anytime you're linking with G++,
02:43that means the G++ libraries are there. You should already think to yourself, maybe it's at least
02:48possible stack alignment could be some of my crashes, especially if you're actually calling
02:53GCC libraries or a library that you don't control or a library that you don't know what the source is
02:59inside of it. So I'm just, you know, I have a little menu here and then there's like a run
03:04target. And then all I'm doing is I'm just compiling a driver and an assembly module called
03:10WHOOPS, which will do the stack alignment issues for us, which will show us the issues.
03:15The driver is pretty simple. It's the entry point for the program. It's got a main function.
03:20It just prints a message and then it calls our WHOOPS function just to prove to you we're in a
03:24hybrid program. We've got an extern C block to disable name mangling on the WHOOPS program so that
03:30the C++ module can call on the assembly module. That's not what this video is about. Check my other
03:36videos if you're interested in more of that, or if you're still trying to figure out what to do.
03:41But basically in the assembly module,
03:43I, you know, define some stuff so that I can print string messages. This is all covered in other videos
03:49that I've made, but we're just going to say there's going to be a welcome message. And then I'm going to
03:54actually call on a GCC library, a regular like C standard library, printf to just kind of print
04:03something for us. Then we're going to do a goodbye message and then we sort of enter up here. So the
04:10first thing you probably are wondering is, wait a minute, can you call printf from assembly? Yeah,
04:15you can. If you're linking with the GCC libraries, there are a lot of functions out there that provide
04:20extra functionality. Just be very careful that you don't accidentally, like if you're doing homework
04:25at home, be careful. You don't actually, you don't accidentally get in trouble for using a function
04:31that was pre-written in order to accomplish something in your homework that you were supposed
04:35to do. So be careful about that. You could, you know, depending on your professor, you could lose
04:41a lot of points or all the points or whatever. So anyway, you can call printf and other C and C++ library
04:49functions. All you have to do is number one, make a hybrid program that links against the GCC libraries
04:54and number two, name the functions with extern. So I want to call printf in this program. So I'm just
05:01going to say extern printf to indicate to the linker that this whoops module should be able to see the
05:07printf symbol, which, you know, it should, should link to it because it's like in another module
05:13somewhere. It'll be inside the GCC library. Then the entry point for this particular module,
05:19it's just a function that I expose as global so that other places in the program can call on it.
05:25And I'm just calling it whoops. I am, let's see, writing this as a function, not a regular assembly
05:32label that has to be syscall exited. So that means this is a function where I have to actually return
05:37when I'm finished. This is not a functions video. But again, if I was using any callee preserved
05:44registers per the ABI, then I would do like a push pop pair on the top and the bottom, but I'm not. So
05:51first thing that happens is we just print a welcome message. No problem. This is not a printing
05:57tutorial video. So check out my other videos.
06:02And then we'll actually just call on printf. We'll ask printf, could you print this string for us?
06:06And then we'll say goodbye with just another print. So this is the problem here, the call to printf.
06:14It seems good. I mean, all we're doing is we are respecting the ABI by saying, all right, printf
06:19wants a pointer to the C string that we wish to print. And so I'm just going to load up the first
06:24integer argument, which also could be a pointer argument, RDI, with a pointer to my string,
06:31just printf message. And then I'm going to call printf. Should work. Let me show you what is
06:37actually in the string, just for your information. The string is just a message in quotes that says,
06:45you know, this is a string printed by printf. And then I add a carriage return line feed to the end
06:50of it. So printf will do the new lining all for me without me having to call another function. And then
06:55I give it a null terminator. I'm not going to explain null terminators too much in this video,
06:58but just keep in mind, this kind of thing is required for printf because printf will scan.
07:03Printf doesn't actually need the length of the message. So I don't even know why I put this in
07:07here. So line 24, totally useless. Anyway, so this feels like it should work, but it's not going to work.
07:15Let's see what happens. If I do, well, I'll do clear and make run, then you can see that the
07:24program gets compiled. And then, um, uh, we get the welcome message from the driver. It says,
07:32my name is Dallas pepper drop. That's not my real name. I just love those names.
07:36And, uh, then it says, I will now call the whoops module. That's the driver talking. And then now we're
07:42inside of the whoops module and it says, hello, this is the whoops module. And then we get a sake
07:46fault program crashes. Your program doesn't work for your customers or you don't get your points on
07:53your homework or whatever it is, uh, because we have a segmentation fault. This is not good. And
07:59this happens to lots of people. They're coding and they're debugging for hours and hours and hours,
08:03not understanding where the sake fault is coming from. It might be coming from stack alignment.
08:08In this case, I know for sure it's coming from stack alignment just because I wrote the program.
08:12On purpose to crash. Um, so let's, let's look at a way to, uh, I mean, first to debug this,
08:20your first clue is I am sure that it's working. It's still crashing and I'm calling something that
08:26is GCC or I'm calling something that I don't know what the code is inside. It could be calling GCC. Like
08:33if you have a library from somewhere else, maybe it's calling GCC. You don't really know.
08:36Um, so that's my first clue. I'll go into the source code and I'll just kind of try something.
08:44So at the beginning of my function, we know that every time you jump into a function or do
08:48a function call, then the stack changes a little bit because the stack has to store the return
08:52address of where you're going to return to later. So that means there's a potential here for stack
08:57alignment issues. And, um, I'm going to go ahead and try something to see if I can prove to myself
09:04that this is a stack alignment issue. What I'm going to do is I'm just going to move the stack
09:09by eight bytes. So how do we do that? You could either move the stack pointer forward and back if
09:15you wanted to, like we have, um, uh, add, uh, or subtract. I think subtract would be what we want to do
09:22just to the, let's see, where is it? RSP, the stack register. I'm not going to do that here. I'm just
09:26going to prove a point and make things a little bit easier. We'll push the number or sorry. We won't push
09:32a number. We'll push RAX. And then after that we'll pop RAX. And what's happening is I'm changing
09:39the alignment of the stack and then I'm calling on print F and then I'm changing the alignment of
09:44the stack again. I'm sort of like, you can imagine I'm just like pushing a dummy value onto the stack.
09:51Like we don't really care what's inside of RAX. We're not trying to preserve RAX. We're not going to
09:56use the stack or anything like that. So I'm just saying, well, let's just put something on there.
10:02I don't even care what it is. And then when we're done, we'll just pop it right back off. That way,
10:07when we're finished, the stack is really unchanged, but inside of that push pop pair,
10:12the stack is differently aligned by about eight bytes because, well, this right here is an eight
10:18byte register, a 64 bit register. So we're moving the stack, then we're doing the call and then we're
10:23moving the stack back run with only that one change. You don't want to make the mistake of
10:27making a bunch of changes because then it's hard to figure out which change actually worked
10:31with just that one change. I'm going to run the program again. Notice how the program finishes
10:36correctly. So of course, nothing in life is 100%, but at this point I would be pretty sure that my issue
10:44was stack alignment. I don't know where the stack alignment came from. Maybe it came from the function
10:48call. Maybe it came from some push pops I had at the top or the bottom. I don't
10:52really know. You can probably bet that if you call on a, on another function that respects the ABI,
10:59that when your function comes, when your call comes back, the stack's not going to be in a different
11:03alignment, which means let's say I had like many, many calls to print F here. I wouldn't necessarily
11:10want to like move the stack back and forth every single time I called on a function. I probably would
11:14want to put this push pop pair like at the very top in the prologue. Maybe let's say prologue.
11:20I'll do like, just push RAX and then leave myself a comment because it's easy to forget assembly in
11:26assembly, like what you're doing. So I'm just going to leave myself a note and I'm just going to stay
11:30set, say stack alignment, just to remind myself not to accidentally remove it later.
11:34And then I'm going to do a pop RAX at the, at the end. So I'm going to do like an epilogue.
11:38Epilogue. Epilogue. I thought I spelled that right. Epilogue.
11:49I am sure that I'm spelling it right. Okay. So then I'm going to pop RAX and, and hilariously,
11:56since you don't really care about the value of RAX, you could pop a different register as long as it
12:00wasn't Kali saved, but just so this is a little bit easier to understand and more stable. I want
12:06to do it this way. So then I'm going to say a stack alignments and maybe I want to like line up these
12:12comments. So I'll just kind of like line up the comments. So now the whole entire function,
12:18because if you look inside of the, the, you know, the body of my function, I'm not really changing the
12:23stack alignment. I'm not modifying the stack register. I'm not doing any more push pop pairs. I'm not doing
12:28anything. So probably the stack is out of alignment during the entire function, which means I can make
12:34my function a little bit faster now by removing these push pop pairs that are surrounding that
12:41surround the print F calls. So, you know, I have like half the number of push pop pairs at this point.
12:49So let's see if that works. Notice how the program still runs. No problem. And just as another experiment,
12:57I'm going to comment out the push and the pop pair, you got to comment out the pair, not just one
13:04push and not the pop or vice versa. Otherwise the whole program is not going to work.
13:09So if I commented it out now, it should crash for sure. Notice how it crashes.
13:15All right. So if I put it back one more experiment just to be, you know,
13:20just to be like really obsessed with the specificity and make sure that the program works
13:26just to be really robust and it works. Okay. So this is a stack alignment in x86 64
13:32GCC library dependent hybrid programs in Yasm. I'm sure this applies to a lot of other areas, but
13:39Yasm x86 64 on Linux is what I'm doing. So I hope this was helpful to you. I hope you learned a little
13:46bit and had a little bit of fun. I'll see you in the next video.
13:53Hey everybody. Thanks for watching this video again from the bottom of my heart. I really appreciate it.
13:57I do hope you did learn something and have some fun. If you could do me a please, a small little favor,
14:03could you please subscribe and follow this channel or these videos or whatever it is you do on the
14:09current social media website that you're looking at right now. It would really mean the world to me and
14:13it'll help make more videos and grow this community. So we'll be able to do more videos,
14:18longer videos, better videos, or just I'll be able to keep making videos in general. So please
14:23do do me a kindness and uh, and subscribe. You know, sometimes I'm sleeping in the middle of the
14:28night and I just wake up because I know somebody subscribed or followed. It just wakes me up and
14:33I get filled with joy. That's exactly what happens every single time. So you could do it as a nice favor
14:38to me or you could, you could troll me if you want to just wake me up in the middle of the night, just
14:41subscribe and then I'll, I'll just wake up. I promise that's what will happen. Also, uh, if you
14:47look at the middle of the screen right now, you should see a QR code, which you can scan in order to
14:52go to the website, which I think is also named somewhere at the bottom of this video. And it'll take
14:56you to my main website where you can just kind of like see all the videos I published and the services
15:01and tutorials and things that I offer and all that good stuff. And, uh, if you have a suggestion
15:08for, uh, uh, clarifications or errata or just future videos that you want to see,
15:14please leave a comment. Or if you just want to say, Hey, what's up, what's going on? You know,
15:18just send me a comment, whatever. I also wake up for those in the middle of the night. I get,
15:22I wake up in a cold sweat and I'm like, it would really, it really mean the world to me. I would really
15:27appreciate it. So again, thank you so much for watching this video and, um, enjoy the cool music
15:34as, as I fade into the darkness, which is coming for us all.
Recommended
34:13
|
Up next
50:14
0:29
0:41
1:00
Be the first to comment