I'm faced with the problem of printing (in console) a value without using any library. I tried to do so by means of the returning value of main function but it seems that there is not a way to show in console the return value of a function without the use of printf(). I'm looking a piece of advice so feel free to 'brainstorm'. Any idea is welcomed, hope you can help me and thank you before hand.
2 Answers
Technically, write (note write and not fwrite) isn't a library function, it's a system call. stdout (which is what's going to appear on the screen), can be written to if you're able to find it's file descriptor number (hint hint, STDOUT_FILENO) using write.
Hope that's somewhat helpful! If you need some more direction feel free to drop a comment!
4 Comments
0, 1, 2 are provided through the defined macros STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO. You hint at it, but always make clear the difference between a file descriptor and file stream pointer (e.g. FILE*). That said, you certainly provided him with good information to follow up on.void* parameter to write() is encountered :)Well "technically" what you call when you say write() is still "technically" in the standard library (although it may be some macro magic in .h files depending on your compiler). There are stub functions that call each system call.
"Technically" you'd need to know your ABI and you'd need to know how to invoke that ABI in your compiler. Then you'd need (generally, for most ABIs) what the current syscall number of write is and so on.
Looking it up, on linux (which isn't my home planet, but it has a chance of being yours) SYS_WRITE is syscall #1 on linux. You put that one in %rax, the fd in %rdi, the buffer pointer in %rsi and the count of characters in %rdx.
So here we go:
mov edx,4 ; message length mov ecx,msg ; message to write mov ebx,1 ; file descriptor (stdout) mov eax,4 ; system call number (sys_write) int 0x80 ; call kernel Those of you reading along at home might remember your BIOS saying something about INT 80 once-upon-a-time. It's the same INT 80.
Now you just need to get that assembly into your C file. Looks like with gcc you use asm
so ...
main() { __asm__("mov edx,4 ; message length" "mov ecx,msg ; message to write" "mov ebx,1 ; file descriptor (stdout)" "mov eax,4 ; system call number (sys_write)" "int 0x80 ; call kernel"); } Now... I'm not telling you here how to get your C string into the registers, but again, I'm saying "Technically" ...
NB: also, note that I've copied from two different examples here to show some code to make my point. The register names on X86 are a mess and I think the paragraph is using different naming conventions than the code snippet.
NB2: to be even more pedantic, "Technically" crt.o might be considered the standard library. It depends. Sometimes it's also a linker script. It's certainly something your compiler toolchain is providing you. I'm not going to copy it here, but for whatever platform you're interested in, google "smallest binary" or somesuch. Lots of people have done this sort of thing for their "channels" ...