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.
-
Only possible with implementation provided functions. – chux - Reinstate Monica Jun 09 '21 at 04:38
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!

- 366
- 3
- 9
-
Always nice to explain how the standard file descriptor numbers `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. – David C. Rankin Jun 09 '21 at 03:53
-
@David This is true, but it seems like this is a homework problem, so I figured I'd let them do a little digging after pointing them in the direction of the POSIX IO API ;) – Mitchell Jun 09 '21 at 04:34
-
No disagreement there, but adding the defines and numbers still leaves understanding the file descriptor use, the syscall use and validation (which can be daunting enough when the `void*` parameter to `write()` is encountered `:)` – David C. Rankin Jun 09 '21 at 06:24
-
You're right Mitchell, it's a homework problem. I'm gonna do some digging out there to figure out the solution but your info is pretty useful. Thanks! – neil_huygens Jun 09 '21 at 13:06
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" ...

- 334
- 1
- 11
-
1This is an unsafe use of GCC's inline assembly; the compiler isn't expecting you to modify random registers without informing it. See https://stackoverflow.com/questions/5131568/writing-a-linux-int-80h-system-call-wrapper-in-gnu-c-inline-assembly?noredirect=1&lq=1 for how to do it correctly. I know it's just an illustration, but it's close enough to working that someone might actually try to use it. – Nate Eldredge Jun 09 '21 at 04:39
-
Appreciate it, this was the sort of "brainstorming" that I was expecting. Have a good one! – neil_huygens Jun 09 '21 at 13:01
-
Yeah... I realize that Nate. In a way, it wasn't part of the asked question. Probably wasn't even the answer the supplicant wanted, per se. I like going down rabbit holes. Barriers to this working: 1. doing what Nate said. Might not matter since main does nothing. I suppose main's return might be the syscall's return ... or gcc might stomp all over it. 2. you'll need to get the address of the string into the assembly msg symbol. Declaring it in assembly would do it. Declaring it as extern in C _might_ do it. – zBeeble Jun 09 '21 at 16:21
-
3. it appears that gcc uses the "other" assembly syntax. Again "linux" and "gcc" are not my jam. – zBeeble Jun 09 '21 at 16:28
-
(heh... just reading the big posts about homework questions. I suppose this is me being a bit of a s**t disturber, too. If the professor had assigned this to me, I probably would have turned in a working version along the lines I spec'd. I probably would have tried to figure out how to get C to behave without crt.o (at least now --- I'm not sure I knew about crt.o in University (but I did know about syscalls))) – zBeeble Jun 09 '21 at 17:30
-
On the contrary I appreciate your enthusiasm. I don't know about syscalls but I'm doing some research now – neil_huygens Jun 09 '21 at 18:36
-
I wasn't familiarized with many of the terminology you've used btw. You've pulled me into test haha – neil_huygens Jun 09 '21 at 18:37
-
crt.o is typically the code that your system links into your executable to make it run and look like C should. crt.o calls main(). It may also parse arguments and allocate system resources. It depends on the system. – zBeeble Jun 10 '21 at 03:42
-
a syscall is how (on an operating system with privilege separation) does syscalls. Strangely on DOS also (dunno why). By loading registers with values and causing an interrupt, the CPU is now in supervisor mode, and the kernel can do it's thing. – zBeeble Jun 10 '21 at 03:44