Printing strings in Assembly, calling that function in C [duplicate] - c

This question already has an answer here:
What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?
(1 answer)
Closed 2 years ago.
all. I was trying to get into programming with NASM, and I also wanted to learn how to make those functions callable in C. I am fairly certain the code that I have so far is correct, in that I need to set up a stack frame, and then undo that stack frame before I return from the routine. I also know I need to return a zero to ensure that there were no errors. I am using debian linux as well, in case I need to adjust for my OS.
The code:
global hello
section .data
message: db "Hello, world!",0 ; A C string needs the null terminator.
section .text
hello:
push rbp ; C calling convention demands that a
mov rbp,rsp ; stack frame be set up like so.
;THIS IS WHERE THE MAGIC (DOESN'T) HAPPEN
pop rbp ; Restore the stack
mov rax,0 ; normal, no error, return value
ret ; return
I feel as if I should point out that I ask this because all of the programs I found made external calls to printf. I do not wish to do this, I would really like to learn how to print things in assembly. So I suppose my questions are: What are the calling conventions for C functions in NASM? How do I print a string in NASM 64bit assembly?
Also, to make sure I have this part right, is this the proper way to call the assembly function in C?
#include <stdio.h>
int main() {
hello();
return 0;
}
EDIT: Okay, I was able to work this out. Here's the assembly code. I assembled the .asm file along with the .c file using nasm -f elf64 -l hello.lst hello.asm && gcc -o hello hello.c hello.o
section .text
global hello
hello:
push rbp ; C calling convention demands that a
mov rbp,rsp ; stack frame be set up like so.
mov rdx,len ; Message length
mov rcx,message ; Message to be written
mov rax,4 ; System call number (sys_write)
int 0x80 ; Call kernel
pop rbp ; Restore the stack
mov rax,0 ; normal, no error, return value
ret
section .data
message: db "Hello, world!",0xa ; 0xa represents the newline char.
len: equ $ - message
The relevant C code (hello.c) looked like this:
int main(int argc, char **argv) {
hello();
return 0;
}
Some explanations include the lack of an #include, due to the I/O being done in the assembly file. Another thing that I needed to see to believe was that all the work was not done in assembly, as I did not have a _start identifier, or whatever that's called. Definitely need to learn more about system calls. Thank you so much to everyone who pointed me in the right direction.

As was cleared up in comments, any interaction between the world outside and your code is done through system calls. C stdio functions format text into an output buffer, then write it with write(2). Or read(2) into an input buffer, and scanf or read lines from that.
Writing in asm doesn't mean you should avoid libc functions when they're useful, e.g. printf/scanf. Usually it only makes sense to write small parts of a program in asm for speed. e.g. write one function that has a hot loop in asm, and call it from C or whatever other language. Doing the I/O with all the necessary error-checking of system call return values would not be very fun in asm. If you're curious what happens under the hood, read the compiler output and/or single-step the asm. You'll sometimes learn nice tricks from the compiler, and sometimes you'll see it generate less efficient code than you could have written by hand.
This is a problem:
mov rax,4 ; System call number (sys_write)
int 0x80 ; Call kernel
Although 64bit processes can use the i386 int 0x80 system call ABI, it is the 32bit ABI, with only 32bit pointers and so on. You will have a problem as soon as you go to write(2) a char array that's on the stack (since amd64 Linux processes start with a stack pointer that has the high bits set. Heap memory, and .data and .rodata memory mapped from the executable are mapped into the lower 32b of address space.)
The native amd64 ABI uses syscall, and the system call numbers aren't the same as the i386 ABI. I found this table of syscalls listing the number and which parameter goes in which register. sys/syscall.h eventually includes /usr/include/x86_64-linux-gnu/asm/unistd_64.h to get the actual #define __NR_write 1 macros, and so on. There are standard rules for mapping arguments in order to registers. (Given in the ABI doc, IIRC).

Related

Printing a value to console avoiding the usage of standard libraries

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.
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!
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" ...

int 80 doesn't appear in assembly code

Problem
Let's consider:
int main(){
write(1, "hello", 5);
return 0;
}
I am reading a book that suggests the assembly output for the above code should be:
main:
mov $4, %eax
mov $1 %ebx
mov %string, %ecx
mov $len, %edx
int $0x80
(The above code was compiled with 32 bit architecture. Passing arguments by registers isn't caused by '64 bit convention passing arguments by registers' but it is caused by the fact, we make a syscall. )
And the output on my 64 bit Ubuntu machine with: gcc -S main.c -m32
is:
pushl $4
pushl $string
pushl $1
call write
My doubts
So it confused me. Why did gcc compile it as "normal" call, not as syscall.
In this situation, what is the way to make the processor use a kernel function (like write)?
I am reading a book that suggests the assembly output for the above code should be ...
You shouldn't believe everything you read :-)
There is no requirement that C code be turned into specific assembly code, the only requirement that the C standard mandates is that the resulting code behave in a certain manner.
Whether that's done by directly calling the OS system call with int $80 (or sysenter), or whether it's done by calling a library routine write() which eventually calls the OS in a similar fashion, is largely irrelevant.
If you were to locate and disassemble the write() code, you may well find it simply reads those values off the stack into registers and then calls the OS in much the same way as the code you've shown containing int $80.
As an aside, what if you wanted to port gcc to a totally different architecture that uses call 5 to do OS-level system calls. If gcc is injecting specific int $80 calls into the assembly stream, that's not going to work too well.
But, if it's injecting a call to a write() function, all you have to do is make sure you link it with the correct library containing a modified write() function (one that does call 5 rather than int $80).

why we must recompile a c source code for a different os on the same machine?

When I compile my c source code (for example in a Linux environment) the compiler generates a file in a "machine readable" format.
Why the same file is not working on the same machine under a different operating system?
Is the problem about the way we "execute" this file?
Sometimes it will work, depending on the format and the libraries that you use, etc.. For example, things like allocating memory or creating a window all call the OS functions. So you have to compile for the target OS, with those libraries linked in (statically or dynamically).
However, the instructions themselves are the same. So, if your program doesn't use any of the OS functions (no standard or any other library), you could run it on another OS. The second thing that is problematic here is executable formats.. Windows .exe is very different from for example ELF. However, a flat format that just has the instructions (such as .com) would work on all systems.
EDIT: A fun experiment would be to compile some functions to a flat format (just the instructions) on one OS (e.g. Windows). For example:
int add(int x, int y) { return x + y; }
Save just the instructions to a file, without any relocation or other staging info. Then, on a different OS (e.g. Linux) compile a full program that will do something like this:
typedef int (*PFUNC)(int, int); // pointer to a function like our add one
PFUNC p = malloc(200); // make sure you have enough space.
FILE *f = fopen("add.com", "rb");
fread(p, 200, 1, f); // Load the file contents into p
fclose(f);
int ten = p(4, 6);
For this to work, you'd also need to tell the OS/Compiler that you want to be able to execute allocated memory, which I'm not sure how to do, but I know can be done.
I have been asked what is an ABI discrepancy. I think it's best to explain over a simple example.
Consider a little silly function:
int f(int a, int b, int (*g)(int, int))
{
return g(a * 2, b * 3) * 4;
}
Compile it for x64/Windows and for x64/Linux.
For x64/Windows the compiler emits something like:
f:
sub rsp,28h
lea edx,[rdx+rdx*2]
add ecx,ecx
call r8
shl eax,2
add rsp,28h
ret
For x64/Linux, something like:
f:
sub $0x8,%rsp
lea (%rsi,%rsi,2),%esi
add %edi,%edi
callq *%rdx
add $0x8,%rsp
shl $0x2,%eax
retq
Allowing for different traditional notations of assembly language on Windows and Linux, there obviously are substantial differences in the code.
The Windows version clearly expects a to arrive in ECX (lower half of the RCX register), b in EDX (lower half of the RDX register), and g in the R8 register. This is mandated by the x64/Windows calling convention, which is a part of the ABI (application binary interface). The code also prepares arguments to g in ECX and EDX.
The Linux version expects a in EDI (the lower half of the RDI register), b in ESI (the lower half of the RSI register), and g in the RDX register. This is mandated by the calling convention of System V AMD64 ABI (used on Linux and other Unix-like operating systems on x64). The code prepares arguments to g in EDI and ESI.
Now imagine that we run a Windows program which somehow extracts the body of f from a Linux-targeted module and calls it:
int g(int a, int b);
typedef int (*G)(int, int);
typedef int (*F)(int, int, G);
F f = (F) load_linux_module_and_get_symbol("module.so", "f");
int result = f(3, 4, &g);
What is going to happen? Since on Windows functions expect their arguments in ECX, EDX and R8, the compiler will place actual arguments in those registers:
mov edx,4
lea r8,[g]
lea ecx,[rdx-1]
call qword ptr [f1]
But the Linux-targeted version of f looks for values elsewhere. In particular, it is looking for the address of g in RDX. We have just initialized its lower half to 4, so there are practically nil chances that RDX will contain anything making sense. The program will most likely crash.
Running Windows-targeted code on a Linux system will produce the same effect.
Thus, we cannot run 'foreign' code but with a thunk. A thunk is a piece of low-level code which rearranges arguments to allow calls between pieces of code following different sets of rules. (Thunks may probably do something else because the effects of ABI may not be limited by the calling convention.) You typically cannot write a thunk in high-level programming language.
Note that in our scenario we need to provide thunks for both f ('host-to-foreign') and g ('foreign-to-host').
There are two things of importance:
the development environment;
the target platform.
The development environment's compiler generates an object file with machine code and references to functions and data not contained in the object moule (not defined in the source file). Another program, the linker, combines all your object modules, plus libraries, into the executable. Please note:
The format of the object module is in principle platform Independent, although standards exist for platforms to easily combine object modules produced by different compilers for the platform. But that doesn't need to be; a fully integerated development environment can have its own "standard".
The linker can be a program from any manufacturer. It needs to know the format of the object modules, the format of the libraries and the desired format of the resulting excutable. Only this latter format is platform dependent.
The libraries can be in any format, as long as there is a linker that can read them. BUT: the libraries are platform dependent as the functions in the library call the API of the operating system.
A cross-development environment could for example generate object modules that are Windows compatible, then a linker can link them with libraries in Windows compatible format, yet targeted for Linux (using Linux OS calls) and deliver a Linux executable. Or any combination you like (Linux object format, windows library format, Windows executable; ...).
To summarize, the only truly platform dependent items are the functions in the libraries, as these call the OS, and the resulting executable as that is what the OS will load.
So, to answer the question: no, there is not necessarily a need to compile a source file for different platforms. The same object module can be linked for Linux (using Linux targeted libraries and creating a linux-format executable), or for Windows (using Windows targeted libraries and creating a Windows-format executable).
Different operating systems will use different Application Binary Interfaces (ABIs), this includes code needed for function entry and exit
Certain language features may need direct platform support (things like thread local storage come to mind)
The linker will generally link automatically to the toolchain specific standard library. This will need to change between Operating systems is for no other reason that each operating system has its own set of system calls.
Having said that, the Wine project is a good example where all these issue have been wrapped up to try to make windows code run on linux.
You are right, compiling translates your source code into machine readable code, e.g. into x86 machine code.
But there is more to it than that. Your code often not only uses machine code that is compiled into your executable file, but also references operating system libraries. All modern operating systems supply different APIs and libraries to the programs. So if your program is built to work with e.g. some Linux libaries and is then executed under an operation system that doesn't contain these libraries it will not run.
The other thing here is the executable file format. Most executable files contain more than just executable machine code, but also some metadata, e.g. icons, information about how the file is packed, version numbers and quite a bit more.
So by default, if you run e.g. a Windows .exe file on Linux, the operating system would not be able to handle that different file format correctly.
Systems like Wine add the missing libraries and are able to handle the different executable file formats, thus allowing you to run e.g. a Windows .exe file on Linux as if it was run on Windows natively.
There are several good, general answers here. I'll give you a very specific example.
A x86 machine can easily run printf("Hello world") both 32bit Linux and DOS, if the C file is compiled for each platform.
One of many major differences between operating systems is how a program instructs the operating system to provide the services it does. Here is how you ask Linux to print a string:
msg db "Hello world" # Define a message with no terminator
mov edx, 11 # Put the message length in the edx register
mov ecx, msg # Put the message address in ecx
mov ebx, 1 # Put the file descriptor in ebx (1 meaning standard output)
mov eax, 4 # Set the system call to 4, "write to file descriptor"
int 80h # Invoke interrupt 80h to give control to Linux
Here is how you ask DOS to print the same string:
msg db "Hello world$" # Define a message terminated by a dollar sign
mov dx, msg # Load the message address into dx
mov ah, 9 # Set the system call number to 9, "print string"
int 21h # Invoke interrupt 21h to give control to DOS
They both use the same kind of basic, machine readable and executable instructions, but the directions are as different as English and Chinese.
So can't you teach Linux how to understand directions intended for DOS, and run the same file on both? Yes you can, and that's what DosEmu did back in the day. It's also how Linux+Wine runs Windows software, and how FreeBSD runs Linux software. However, it's a lot of headache and additional work, and may still not be very compatible.
I post this reply to Andrey's discussion about ABIs as an answer because it is too much for a comment and requires the formatting of an Answer.
Andrey, what you are showing has nothing to do with Linux or Windows. It is an example of a development environment using certain conventions. All object modules and modules in libraries must adhere to these conventions, and nothing else. It isn't Linux or Windows that expect values in certain registers, it is the development environment.
The following is the more standard way of C calling conventions (Visual Stdio 2008). In all cases, the caller must evaluate parameters right-to-left as per the C standard:
int f(int a, int b, int (*g)(int, int))
{
push ebp
mov ebp,esp
return g(a * 2, b * 3) * 4;
mov eax,dword ptr [ebp+0Ch]
imul eax,eax,3
push eax
mov ecx,dword ptr [ebp+8]
shl ecx,1
push ecx
call dword ptr [ebp+10h]
add esp,8
shl eax,2
mov esp,ebp
pop ebp
ret
}
The caller pushes the parameters right-to-left and calls the callee
the callee saves the stack frame pointer, usually ebp on Intel, and adds to esp for local storage (none here)
The callee references the parameters relative to ebp
The callee performs its function
The callee restores ebp and returns
The caller removes the parameters of the call from the stack, e.g. add esp,8
Again, it is the development environment that dictates these conventions, not the OS. The OS may have its own conventions for applications to request services. These are then implemented in the OS-targeted libraries.

What is the use of _start() in C?

I learned from my colleague that one can write and execute a C program without writing a main() function. It can be done like this:
my_main.c
/* Compile this with gcc -nostartfiles */
#include <stdlib.h>
void _start() {
int ret = my_main();
exit(ret);
}
int my_main() {
puts("This is a program without a main() function!");
return 0;
}
Compile it with this command:
gcc -o my_main my_main.c –nostartfiles
Run it with this command:
./my_main
When would one need to do this kind of thing? Is there any real world scenario where this would be useful?
The symbol _start is the entry point of your program. That is, the address of that symbol is the address jumped to on program start. Normally, the function with the name _start is supplied by a file called crt0.o which contains the startup code for the C runtime environment. It sets up some stuff, populates the argument array argv, counts how many arguments are there, and then calls main. After main returns, exit is called.
If a program does not want to use the C runtime environment, it needs to supply its own code for _start. For instance, the reference implementation of the Go programming language does so because they need a non-standard threading model which requires some magic with the stack. It's also useful to supply your own _start when you want to write really tiny programs or programs that do unconventional things.
While main is the entry point for your program from a programmers perspective, _start is the usual entry point from the OS perspective (the first instruction that is executed after your program was started from the OS)
In a typical C and especially C++ program, a lot of work has been done before the execution enters main. Especially stuff like initialization of global variables. Here you can find a good explanation of everything that's going on between _start() and main() and also after main has exited again (see comment below).
The necessary code for that is usually provided by the compiler writers in a startup file, but with the flag –nostartfiles you essentially tell the compiler: "Don't bother giving me the standard startup file, give me full control over what is happening right from the start".
This is sometimes necessary and often used on embedded systems. E.g. if you don't have an OS and you have to manually enable certain parts of your memory system (e.g. caches) before the initialization of your global objects.
Here is a good overview of what happens during program startup before main. In particular, it shows that __start is the actual entry point to your program from OS viewpoint.
It is the very first address from which the instruction pointer will start counting in your program.
The code there invokes some C runtime library routines just to do some housekeeping, then call your main, and then bring things down and call exit with whatever exit code main returned.
A picture is worth a thousand words:
P.S: this answer is transplanted from another question which SO has helpfully closed as duplicate of this one.
When would one need to do this kind of thing?
When you want your own startup code for your program.
main is not the first entry for a C program, _start is the first entry behind the curtain.
Example in Linux:
_start: # _start is the entry point known to the linker
xor %ebp, %ebp # effectively RBP := 0, mark the end of stack frames
mov (%rsp), %edi # get argc from the stack (implicitly zero-extended to 64-bit)
lea 8(%rsp), %rsi # take the address of argv from the stack
lea 16(%rsp,%rdi,8), %rdx # take the address of envp from the stack
xor %eax, %eax # per ABI and compatibility with icc
call main # %edi, %rsi, %rdx are the three args (of which first two are C standard) to main
mov %eax, %edi # transfer the return of main to the first argument of _exit
xor %eax, %eax # per ABI and compatibility with icc
call _exit # terminate the program
Is there any real world scenario where this would be useful?
If you mean, implement our own _start:
Yes, in most of the commercial embedded software I have worked with, we need to implement our own _start regarding to our specific memory and performance requirements.
If you mean, drop the main function and change it to something else:
No, I don't see any benefit doing that.

Is it possible to convert C to asm without link libc on Linux?

Test platform is on Linux 32 bit. (But certain solution on windows 32 bit is also welcome)
Here is a c code snippet:
int a = 0;
printf("%d\n", a);
And if I use gcc to generate assembly code
gcc -S test.c
Then I will get:
movl $0, 28(%esp)
movl 28(%esp), %eax
movl %eax, 4(%esp)
movl $.LC0, (%esp)
call printf
leave
ret
And this assembly code needs linking to libc to work(because of the call printf)
My question is :
Is it possible to convert C to asm with only explicit using system call automatically, without using libc?
Like this:
pop ecx
add ecx,host_msg-host_reloc
mov eax,4
mov ebx,1
mov edx,host_msg_len
int 80h
mov eax,1
xor ebx,ebx
int 80h
Directly call the int 80h software interrupt.
Is it possible? If so, is there any tool on this issue?
Thank you!
Not from that source code. A call to printf() cannot be converted by the compiler to a call to the write system call - the printf() library function contains a significant amount of logic which is not present in the system call (such as processing the format string and converting integer and floating-point numbers to strings).
It is possible to generate system calls directly, but only by using inline assembly. For instance, to generate a call to _exit(0) (not quite the same as exit()!), you would write:
#include <asm/unistd.h>
...
int retval;
asm("int $0x80" : "=a" (retval) : "a" (__NR_exit_group), "b" (0) : "memory");
For more information on GCC inline assembly, particularly on the constraints I'm using here to map variables to registers, please read the GCC Inline Assembly HOWTO. It's rather old, but still perfectly relevant.
Note that doing this is not recommended. The exact calling conventions for system calls (e.g, which registers are used for the call number and arguments, how errors are returned, etc) are different on different architectures, operating systems, and even between 32-bit and 64-bit x86. Writing code this way will make it very difficult to maintain.
You can certainly compile C code to assembly without linking to libc, but you can't use the C library functions. Libc's entire purpose IS to provide the interface from C library functions to Linux system calls (or Windows, or whatever system you're on). So, if you didn't want to use libc, you would have to write your own wrappers to the system calls.
If you compile some C code which does not use any function from the C library (e.g. does not use printf or malloc etc etc....) in the free-standing mode of the GCC compiler (i.e. with -ffreestanding flag to gcc), you'll need either to call some assembler function (from some other object or library) or to use asm instruction (you won't be able to do any kind of input output without making a syscall).
Read also the Assembly HowTo, the x86 calling conventions and the ABI relevant to your kernel (probably x86-64 ABI) and understand quite well what are system calls, starting with syscalls(2) and what is the VDSO (int 80 is not the best way to make syscalls these days, SYSENTER is often better). Study the source code of some libc, in particular of MUSL libc (whose source code is very readable).
On Windows (which is not free software and which I don't know) the question could be much more difficult: I am not sure that the system call level is exactly and completely documented.
The libffi enables you to call arbitrary functions from C. You could also cast function pointers from dlsym(3). You could consider JIT techniques (e.g. libjit, GNU lightning, asmjit etc...).

Resources