 Okay, welcome back to operating system. So we have an air conditioned room for a change. That's nice. Drawback is that it's dripping water over there, so beware. So you take some, you lose some. So where we left off last lecture, we have to wrap up talking about kernel architectures. So there are different kernel architectures and remember that simple thing to remember that the kernel is just the part of the operating system that runs in kernel mode, which is a different CPU mode. So there are different architectures for how much software actually you put into your kernel and actually runs in kernel mode. So the first architecture is just called a monolithic kernel that tries to shove everything as possible into the kernel. So this is the actual architecture of Linux and the idea behind that is, well, system calls are actually quite slow instead of just calling an address and passing things in registers and you don't have to change CPU modes or anything like that. System calls generating and interrupt handling the interrupt is actually really, really slow. So the idea behind this architecture is well, we'll reduce the number of system calls you have to make. We'll make each call do a lot and interact directly with the hardware. So things like dealing with virtual memory, process scheduling, what process is actually running at any particular time, IPC, which is again, inter-process communication, file systems, device drivers, they'll all be done in kernel mode. And again, the idea behind this is reduce the number of system calls, make things fast. But as you might have noticed from lab zero or just having a kernel mode, if you exploit the computer and you can access kernel mode, suddenly you can access everything and any security measure you have doesn't work anymore because you can read all of memory. So the other architecture is called a micro kernel and the idea behind that is to reduce the attack surface. So you try and keep your kernel as small as possible. So at minimum, things that have to be in your kernel are things like virtual memory that has to be in your kernel because it needs that separate CPU mode to manage it to have some security for virtual memory. Also needs to have process scheduling in kernel mode because it has to maintain control of the CPU in order to switch processes and it has to have some basic form of IPC so that any of the requests to communicate between processes have to be explicit. But that is the bare minimum that you have to have in your kernel. Things like file systems, device drivers and more advanced forms of IPC you can do in user space. So the drawback here is that, well, your kernel is really, really small but you might have to do a lot of system calls which is going to be really, really slow but your benefit is that it's smaller so there's less opportunity to actually get it exploited. There's less attack surface because there's much less code. Yep, IPC stands for inter-process communication. So being able to send data between two processes that are otherwise independent. So even printing F, like using print F is a form of inter-process communication because you're seeing the result of your process. Otherwise, if you didn't have inter-process communication you wouldn't be able to see what any process is doing and just be independent and just sit there and kind of work away and you'd never be able to see anything. So there's other types of kernels that kind of blur that line or move that. So there's something called hybrid kernels which are somewhere in between monolithic and micro. So like Windows for example is closer to a monolithic kernel but it'll like do DOS emulation and other emulation in user mode because that's some old things that probably will get exploited. So it doesn't wanna run that in kernel mode and on Mac OS, well they have a very small kernel mostly because Apple doesn't trust you to do anything so they wanna keep their kernel to themselves and don't want you running in kernel mode so any device drivers on Mac OS will actually run in user mode. There's also a form of research if you wanna do like grad school and stuff like that so way off in the future there's something called nano kernels and Pico kernels which kind of just push that boundary of micro kernels and try and make them even smaller and somehow still work and be useful but all of these different architectures have different trade-offs and there's no one answer it just kind of depends what you want to use it for. So from last lecture what we need to take away is that hey, there's a kernel interface it's called you access the kernel through system calls and there's different distinct CPU modes that your CPU operates in. So all you need to know is that the kernel is a part of the operating system that runs in kernel mode which is a distinct CPU mode which can actually manipulate the hardware directly it has access to more instructions than you otherwise have if you just have a normal application and every program has to use a system call interface that's why we have our good friend Strace to figure out what literally any program does. We saw some file format you don't need to actually know the file format that was just to show you what it kind of looks like in magic and like dispel a bit of magic but you will never be tested on the Elf file format or that crazy crap I did. The only thing you need to know is what kernel mode is, system calls being able to use Strace will probably serve you good in your computer career and knowing the different kernel architectures is a good thing. So now we can finally start to answer the or ask the question what actually is an operating system? So the kernel is part of the operating system but what else? So like for instance, A1Y give me their opinion whether Mac OS, iPad OS, watch OS, TV OS, iOS are all different operating systems are they actually the same thing? They're the same thing. All right, who's on team? They're the same thing and it's all marketing crap. Okay, who's on team? They're distinctly different and yay Apple. Yeah, well, okay. I probably shouldn't have said yay Apple because I probably raised some hands but anyways, so they're like in 95% the same probably. So let's kind of discuss what actually is an operating system because there's actually no good definition of an operating system. It kind of depends what you want to do with it. So the thing we actually care about are applications and they might pass through many layers of libraries on your system. So the kernel is just a small part. There are libraries that everyone's used the standard C library but if you're doing graphical applications and stuff like that, well, you might use a display server. So on Linux, the display server is something called Wayland and if you don't want to write just direct graphical buffers and stuff like that, you might want to use a GUI toolkit. So you'd use something like JTK which would be built on top of Wayland and Wayland would probably be built on top of the standard C library and then you can see how these libraries would just kind of cascade and get bigger depending on what you want your application to do. So like Firefox uses JTK to draw some interface things so it would use that. It might also want some low level access to hardware so there's this system daemon that gives you an interface to try and make requesting access to hardware easier so Firefox might use something like that while something like Lever Office might just use the GUI toolkit, Network Manager might just directly interact with the system daemon and each one would probably use a different set of libraries and each application would do something distinctly different and you might want to transfer it between different operating systems. So what an operating system is actually just depends on the application. So for instance, there's Android and Debian, both of those use the Linux kernel but unlike the Apple thing you probably would write away say that Debian and Android are definitely not the same operating system because they use completely different applications but you could actually call them the same operating system if you only use terminal applications. So if you just use the standard C library and anything you wrote on 105 you could actually just execute it on your phone, no problem, your phone has a shell even though it's kind of a pain to access it but in terms of little applications like that you might say Android and Debian are the same operating system because you don't care about Android's window manager or anything like that you're not making an Android app you're just have a little terminal application in which case it would work on Android. So even being able to say Android and Debian are completely different that depends, yep. Yeah, so the question is well on Android I can't install most Linux stuff because I don't have like a package manager like after something like that. So that's because that's just by default Google doesn't want, Google doesn't have a package manager they want you to use their app store because they think you're gonna use applications. So they don't have a package manager but if you really wanted to you could put your own package manager on Android if you really wanted to, just no one does it. And the difference between Linux distributions they're pretty much the same except Linux distributions might use different package managers and the difference between them is basically who's packaging up your software and how you get it. The software is generally the same. So like Debian to Ubuntu it even uses the same package manager. It's just different developers manage all those packages for you and that's why it'll be at different versions depending on it and why you might have some wars between people what distribution they like the better, better but at the other day they're all pretty much running the exact same stuff. So yeah, so that's what ran about there. So Linux distributions might also be something considered GNU slash Linux. So that's like more of a full operating system I guess depending on if you wanna use this term because GNU distributes the standard C library and bunch of the utilities while Linux is just a kernel. So saying the Linux operating system doesn't really make sense because it needs a lot of other things to work even for just terminal applications. So an operating system actually just consists of any kernel and libraries you need to run your application and that's the definition of the operating system which of course depends on what applications you want to run. So now we have to explain what a library is because some of you may have never written a library before but libraries are just written in C and it's just how you use them we'll write some libraries in this course so I should probably explain what they are. So this should be reviewed this is like normal compilation in C if you want to use multiple C files. So if you use multiple C files your compiler will compile each one of them into a .o file and then there'll be something called a linker that essentially smushes them all together to create your executable and then you can execute your executable. So anyone not know this because if you don't know this it's bad. Okay, great. So what static libraries are they're included at link time so whenever you create that executable that's when all the code is included and it basically does the same thing as that normal compilation. So instead of taking all those .o files I could separate out say main was just specific to my application and want to reuse the rest of my C files. Well, I could compile util.c, foo.c, bar.c compile them, make these .o files and instead of including them directly in my executable I can archive them and create this lib.a. So that is just an archive of all essentially just smushes all of the .o files together. So now lib.a will have the contents of util, foo and bar and now my linking step is a bit different so instead of using all those files directly I can just use lib.a. So I can link together main.o and lib.a which has all the other .o files and it creates the executable. So this is exactly the same as normal compilation except I put three of them in lib.a and that way, hey if I want to compile a new executable and use those well I can just change main.o I don't have to recompile anything else I could just use lib.a again. So everyone on the same page with that, yep. So the question is do static libraries always have the extension .a and on Linux they will, yeah. On Windows they'll be, oh why are they on Windows? No, DLL is dynamic libraries. I forget where they are on Windows, there's something but generally people don't really use it that much on Windows. But yeah, okay, so that's static libraries. So basically the same thing as normal compilation except you can split off some of the .o files and reuse them. So this is dynamic libraries and they're meant for truly reusable code. So the standard C library is a dynamic library and on Linux it'll be .so, on Windows it'll be .dll and on macOS it'll be dinelib. Basically it's the same thing it's a collection of .o files containing all the function definitions but it doesn't get included in the executable itself. So for instance libc would be a dynamic library it would have printf and everything inside of it but you don't include the definition of printf in your executable you just use it. So like application one, application two would both use libc so whenever you execute it like we saw when we S traced it the applications open libc whenever they're executed and then find the code to call there and figure it out whenever you actually execute it. This also gives us some nice benefits that the operating system can only load libc once and reuse it for every single application by doing some tricks with virtual memory that we'll get into later but it makes things much more efficient too, yep. So yeah, the question is if I include a static library it'll basically copy and paste the entire library into your code. So the including's not part of the linking phase but yeah, whenever you statically link something it'll basically copy and paste that code and put in your executable. So if you update the library your executable is gonna stay the same it's not gonna get the update which is one of the drawbacks, yep. Good question. Oh, yep. Yeah, so the question is why does it prefer to use a static library rather than dynamic and I'll ask you in a second. So just to make this clear before I ask you that so dynamic libraries they're included at runtime so I would take the same .o files and then stick them together in something called using shared linkage. This .so stands for a shared object so it's meant to be included by multiple things. There's some implementation details that make it different but basically it's just something that is included at runtime. So now whenever I create my executable I just have a main .o and I link it and the compiler needs to find it at link time but it doesn't actually use it, it doesn't actually matter and then whenever you execute your program at runtime it's gonna figure out it's gonna open libc and then run that function there. So there's also this useful command line utility for dynamic libraries if you wanna figure out what dynamic library something uses and that's just for your knowledge it might be useful. So here we get to this. So static versus dynamic libraries so you could statically link your code it essentially copies everything and puts it in the executable so some drawbacks are if you statically link you can't reuse the library anymore because whenever you link it it copies and paste the code in your executable. So if there were some security vulnerability or whatever then you had have no choice but to just recompile your application in order to use a new version of the library that doesn't have that exploit. So you would always have to recompile it you also might get into the situation where a bunch of applications are using that and they could only load it once but because every single application has their own copy and pasted version of that function inside you're wasting a bunch of space so think if you had to put printf in every single executable you'd have like thousands and thousands and thousands and thousands of copies of printf which you probably don't want. So would there be any issues with dynamic libraries? Yep, so first comment is maybe there's some speed issues that you have to load it every time instead of it being loaded in cache so that's a good insight but one advantage of it in some cases it'll actually be faster so like think of the standard C library the first application that loads it will probably be slower but every other application that loads or uses the standard C library will already be in memory so they won't have to load it so some of them could be faster some of them could not it's not quite clear yep, yeah, okay so that's another good comment that like I said if there's some issue then you had to recompile everything well that's a double-edged sword because there could be an issue with one of the updates and then if there's an issue with one of the updates suddenly every single program is now vulnerable so there's a little bit of give and take there and it kind of depends on how good the dynamic library authors are so there's kind of this ebb and flow in computer engineering whether or not people want to use static libraries and dynamic libraries generally, you know, dynamic libraries are good until they degrade in quality and then they suck and then people are like yeah, I'll just do it statically then I know no one can break it and then there's that update issue and then people change back and it's just a whole thing so that's basically the whole idea and dynamic libraries you have to be really good at writing software in order for your dynamic library not to suck so we're going to go through an example where just even a slight tweak can break every single application so we talked a bit about ABI's before while there's a lot of things in C that are actually correspond to an ABI that you might not know so let's consider some dynamic library it has a struct with just one struct in it we'll take a point for example with like two fields and X and a Y so when you write a struct in C and you write the fields the order you write the fields corresponds to how that is actually physically laid out in memory and the order actually matters so if an executable uses a struct that a library has they have to match exactly if you change something and change the memory layout by accident well suddenly you might get in a situation where things disagree and very bad things will happen and it will be frankly impossible to debug because you don't have to change the API at all so think of having a struct with two fields while the order of the fields doesn't matter in terms of the API right if it has an X and a Y it doesn't matter if it's Y and X or X and Y doesn't matter but in terms of the ABI it does matter so we'll see how that works so let's just dive into the example so let's say we have a point again I said structs are laid out in memory in order of the fields and the C compilers all agree for this this is why you can use multiple C compilers and everything seems to work so if I have something like this I have library version one I have struct point where I created it as YX and then later on I decided to release version two of my library where I thought that was a bit silly and I should have it in the order XY so this corresponds to very different memory layouts so each struct has two ints so it takes eight bytes but for this struct Y comes first and then X so it would start at Y would start at byte zero and then X would start at byte four and then for this struct X would start at byte zero and then Y would start at byte four so if that's a bit confusing so yeah so and here an offset so what I'm saying byte four it starts four bytes from the beginning that's what an offset is an offset is just how many bytes since the beginning is that is the start of the next data structure so I'll present it a bit differently here because that might be confusing so for both of them for struct the struct in version one and version two they could both be represented if it's easier as just an array of two ints takes up the same amount of space and it might be easier to think of it as an array but in the first version here in version zero or sorry version one well Y corresponds to being at array index zero and X corresponds to be at array index one while if I change the order of them it actually would change the order in that array so in version two X is now at array index zero and Y is in array index one so any questions about that? All right it was worth making this figure then cool so here is our library so it's just what functions are defined in it so here we're going to have a point create that takes an X and a Y and returns a pointer to a point struct and then we're gonna have some accessor methods like a struct point that take a pointer to the point and it's supposed to just return whatever the X value is and then whatever the Y value is and then we have this point destroy so that function frees the memory so if this is our program we'll include point.h which will have the struct for the point and those four functions so to use it we could do something like create a point and then we could print off X and Y using the library so we'll just print off whatever we get from get X and then get Y and then we'll also print them off using the struct so this code should always print the same thing right X is one, Y is two so both these lines should print the same thing it should always be one two, one two otherwise things would be flipped and it would be really weird if this was like a graphic engine it would just get flipped and it would be awful so let's go to it so here's my program and I can go ahead and run it so if I compile it with version one of my code and then link it with version one of my code both the versions match so I should get one two both times so this makes sense to everybody we're all good everything works now the bad stuff so on Linux one of the there's some fun environment variables that you might use to change the behavior of the linker at link type or the dynamic linker so the first is LD library path so we can actually simulate an update and we can set LD library path to version two to simulate an update where it will use that version instead so we can go ahead and go back so now with this command we're gonna run the exact same executable that we compiled with version one and linked with version one which worked before but we're going to set this LD library path to version two so that means whenever I execute this it will look for lib point in version two and this is gonna simulate updating the library and again the libraries are otherwise the same I just flip the order of two fields so now it's screwed up so now the first line is right using the library is fine that's one and two but when I used the field directly it screwed up it swapped them which is not good so you could imagine if this was your application one day you're going along running it it's all good the next day you update your system and then suddenly the whole thing flips on you and goes upside down or whatever I forget linear algebra but it does some translation where bad things happen okay, any questions for that? Yep, so here using the library is this line where we're using point get x and point get y and then here we're just using the fields directly so I will, so if that doesn't make sense, oh yep, okay so here's, whoops, cool here's what kind of it looks like so in this case I have executables on the left and then libraries on the right so in my executable if I compile with version one it doesn't matter what actually I link it with whenever I compile it because it's dynamic, it doesn't matter so when I have the print call for the library well it's just calling into the library I'm actually not including anything here but because I use that struct that struct access will get compiled into my executable so for the print line for the struct that x translates into array index one and y translates to array index zero and that's compiled directly into my executable so now if I use the library version one lib point will use this over here so x will be array one, y will be array zero whenever it does create because the code for create is actually in my library which gets loaded at runtime so create is going to use x and y and then point get x is gonna do array index one point get y is gonna use array index zero and now these two if I have executable v one library v one they both agree with each other so if they both agree with each other I get the correct result now the problem is when they both don't agree with each other so if I still use my executable compiled with v one and use version two of the library well now when point create happens point create thinks x is at array zero and y is at array one and whenever it does point get x it's at array zero which matches what it created so it's gonna be correct and point get y matches what it created of course so it's gonna be correct but now the executable the indexes embedded in the executable don't agree with the library and that's why they flip so there's all different combinations so even if I use, let's go back, whoops so if I use point example compiled with version two and link with version two it works because they both agree but I could compile it with version two and link it with version zero say this was a really old system well it's gonna have the mismatch as well so it's also going to flip yep yes yeah so this is less of an issue if you're just always using the API correctly and I could ensure this and see if I just in my library I never let you see a struct then it works the same as like in Java or something right it's hidden from you so that's why using pointers is a thing because it hides information from you so if I never tell you what the struct looks like I can change it the second I tell you what the struct looks like I suddenly can't change it ever I can add to it I can never take away from it a reorder yep so which one this which slide this oh whoops oh so this has everything here so this is the executable compiled with version one this is the executable compiled with version two and then this is library version one and library version two so if I have the executable compiled with version two and I use library version one there'll be a mismatch it'll be wrong but if I use the executable compiled with version two and the library compiled with version two they'll be matched so it'll work fine so this was actually the cause of a really bad problem where the developers of the standard C library no it was C++ changed the link list structure and forced every single person on the planet to recompile all their programs because they broke the ABI by accident and if they do that your code suddenly starts misbehaving any link list just kind of died and then you have to recompile everything yep so the environment variable I just used oh so the question is where in the code actually use this LD library path? So if you S trace it resolving dynamic libraries is a library that's part of the kernel and the kernel figures it out so the kernel actually uses that environment variable to figure it out which library to actually load yeah so I did not make this up to the example this is how on Linux every single thing uses that so if you want to if you really wanted to you could write your own libc and then change LD library path to point to that and then you can use your libc if you really wanted to yeah this problem is only in dynamic libraries because they're loaded whenever you execute it and if you change it from one to another they might disagree and then bad things will happen but if this was static it'd be compiled in and as long as you tested that it worked it works right yep so for point.h it's just like the header file that says what functions are in the library and the struct is also included in there so that's it and then the lib point itself will use that but provide definitions for those functions yep yeah so one thing as well can I make them private instead? well one it's C so private doesn't exist and two even if they were private so say C just had private well even if you change the order and they can't access it in memory they're still going to be in the different order and it's going to disagree so even if there was private there's still that memory thing but if you are using Java which has private well Java also everything's a reference in Java which means it's basically a pointer so you don't get to see how things are laid out anyways so Java doesn't have this problem but Java is also slow and ugly so okay cool so let's get into more fun stuff we can do so yeah so you can compile that there's all the different combinations we execute so if there's any mismatch it would be wrong uh... and they just wouldn't agree so the solution would be if I write a library and I export a struct I'm not allowed to change it ever otherwise really bad things will happen so the Linux kernel developers are really really aware of this so they have one simple rule of like don't break user space which means don't break application code so they will if you try and be a kernel developer and you break something that you'll get yelled at and it'll just be bad so this would be an example that don't get yelled at don't do this easiest thing to do is just don't don't let anyone see your struct and then you can change it if you want because it's only accessed in your library and your library just has to agree with itself which is much easier so there's also this thing called semantic versioning to kind of uh... tell developers what to expect with libraries so it kind of gives you some conventions for version numbers usually they're in the order major dot minor dot patch and they're all just numbers select one point zero point zero or something like that and the rule typically behind these are you increase the major version whenever you make an incompatible API or a b i change and then the idea behind that is if i see the major number go up from version one to version two i know i can no longer link with it i can't use that dynamic library anymore i would have to if i want to use version two i would expect after recompile my code to use version two and then throughout that i should be able to use version two you increment the minor version whenever you add functionality that but you don't break any apis or something that you're only just addition adding stuff to it so like version two point one can have a new feature and then your software knows that hey if i'm using this library i need at least two point one for this feature and i can't use three point anything and then you just increment the patch version whenever you just make a little bug fix and you don't actually add a functionality or do anything with it so developers more or less follow this for libraries yep yeah so depending on the application you can multiple versions of that dll on the machine that's a big issue with windows because typically people distribute the dll with their applications so you can have which is kind of silly because then you have you know ten different versions of the same library that are all slightly different versions and if you ever heard of the term dll hell that's what that is you can get into the case where you have the worst of both worlds so the worst of both worlds would be having a unique dynamic library for every single executable so that's just terrible alright so a nice another feature about dynamic libraries is it's also easier to debug so those ld library path and another one we're going to use called ld preload their environment variables that the dynamic liquor itself uses so you can basically use this to manipulate it to load your library instead of any other library so let's look at this example so here is a simple c program it has a main mallocs malloc is our friend so we're mallocing the size of an int which should be four bytes and we get a pointer to an int because hey it's malloc so now we're going to print off what the memory address is of x we're not going to we're not going to write anything to it and we're going to free it and then return so anyone disagree with this program looks like it's how many mallocs is this program doing yeah one anyone think it's doing two all right okay well let's see so code all right so i run it oh yep oh yeah so i got another question why did i cast it to void pointer and the answer to that is i wanted to make c happy because percent sign p wants to be a void pointer so i just cast it just to get rid of a warning all right so this makes one malloc whenever i print it looks like that so what i can actually do is i can essentially monitor every single malloc this application makes myself so using ld preload preload i can force another library to load before the standard c library so in this case in alloc wrapper i created a function called malloc so my function called malloc logs what happens with malloc and then calls the real malloc so because this is loaded before real malloc any malloc calls your program makes goes to my program instead so using this you could monitor every single malloc call your application makes so let's run the same thing that just has one malloc call so if i wrote this library correctly it should show me that one malloc call oh and i also have it monitoring freeze as well so this program actually calls malloc twice anyone want to explain that to me because that seems kind of weird yep for this normal x like nope close good guess anyone else where the how this calls malloc twice you know we have a get another guest casting to void start is that called malloc again so the answer to that is no casting just makes the compiler happy so doesn't actually do a malloc or anything like that yep exactly so print f uses malloc because it needs to use memory so that's this called the malloc which is 1024 that's actually done by print f which kind of makes sense right print f needs memory likely so this malloc call is actually from print f but you'll notice something funny that i being a good upstanding c programmer my memory here i called free on it so these addresses match but those damn developers of the standard c library they called malloc and didn't call free those jerks geez now i'm going to get blamed for it right so has anyone ever used something called valgrind so valgrind it will help you even though you might not like it so we can go ahead and use valgrind so we're valgrind so if we use valgrind on this program build oops hey it tells us there's two allocations and two freeze but i showed you that that is not true so it says that 100 1028 bytes so print f did 1024 those and i did the other four but if i run valgrind it says there is two freeze which is definitely not true but if you look at the documentation it says this the standard c library which is used by all programs may allocate memory for its own uses usually it doesn't bother to free it when that program ends there would be no point since the kernel reclaims all the processes resource when a process exits anyways it would just slow things down so that is their argument for not calling free that does not excuse you from calling free so you don't have to call free if it's a global variable because it exists as long as the program exists but otherwise you should probably call free but valgrind actually takes these things into account so you don't get any false positives with that so i can't control not freeing something from print f so valgrind does actually make your life a bit easier yep yeah so there's one question well would it even be possible to free memory from print f and yeah the answer to that is no unless you well it's c so if you could somehow guess the pointer it was using you could free it but you would have to guess yep oh yeah so there's a question what does it mean to reclaim all the processes resources so whenever your process ends the kernel will go ahead and free all the memory it used for example it would no longer be able to execute so when it's done it just cleans up everything so all of its memory app or all of its memory and close all the file descriptors and everything like that yep that they have an excuse you don't so you can use their excuse but if you use their excuse you have to be very convincing so you have to say that the memory was going to last the entire lifetime of the process anyway so i didn't need to free it that's your excuse if you can make that go for it yep no so here yeah so the question is if i have a thousand print fs it's going to malloc a thousand times so if we called print f multiple times we would see that it only does a malloc once and then reuses that memory over and over again okay so also really quickly if you don't like valgrin there's another tool that's better called address sanitizer so i'll show you how that looks so let's say we forgot this free so to run address sanitizer you can so we're going to use mason for all of our projects so you can do something like this as the setup step and then it will compile in address sanitizer and whenever that's compiled in whenever i whoops i have to compile first so whenever i execute my program it'll give me a nice summary here of my leaked memory so in this case i forgot to call us free so it says four bytes leaked in one allocation and it will tell you the exact line number that the malloc happened that you didn't free so you might want to use address sanitizer it is kind of cool so that will be set up in the lecture slides so we have another example but i don't know if we'll have time for it so yeah so system calls are rare in c you'll never really make them you're mostly using calls from the standard c library most of them correspond one to one but the function calls in c may do another thing set this global variable called error no buffer read and write so like for print f we know it calls right but you might notice sometimes when you call print f that you don't actually see any display on your terminal well that's because print f will buffer and wait until there's a long enough string and then it would call right so that's another thing the c standard library will do for efficiency again because system calls are slow so if you had like print f a thousand print f's that did one byte at a time while the standard c library is a bit smarter than you it wouldn't make a thousand system calls it would wait for all those bytes and then make one system call for the full thousand bytes might simplify some interfaces or add some new features and we're out of time so just remember pulling for you we're all in this together