To try to display graphics using C, I am trying to take advantage of C's "inline assembly" feature. I get no errors during compilation, but when I try to run the program, I get this error:
Segmentation Fault: 11
Here is my code:
int main(){
asm("movb 0xc,%ah");
asm("movb $1,%al");
asm("movw $5,%cx");
asm("movw $5,%dx");
asm("int $0xc");
return 0;
}
Constructive criticism appreciated, insults not.
Thanks!
First, it looks like you're trying to use BIOS interrupts to do the graphics, but the graphics interrupt is int 10h (0x10), not 0xc, so you want to call int $0x10.
Second, you can't call most BIOS interrupts from within 32-bit or 64-bit Linux or Windows programs, so make sure you're compiling this for DOS. Otherwise, calling the invoke interrupt opcode on a BIOS interrupt will crash your program. And if you run a newer version of Windows, you'll probably still have to run your compiled program inside of an emulator like DOSBox for it to work properly.
Finally, GCC inline assembly has a certain format to it:
__asm__ __volatile__ (
assembler template
: output operands /* optional */
: input operands /* optional */
: list of clobbered registers /* optional */
);
So for example:
int main()
{
/* Set video mode: */
__asm__ __volatile__ (
"movb $0x0, %%ah \n\
movb $0x13, %%al \n\
int $0x10"
:
:
:"ax"
);
/* Draw pixel of color 1 at 5,5: */
__asm__ __volatile__ (
"movb $0xC,%%ah \n\
movb $1, %%al \n\
movw $5, %%cx \n\
movw $5, %%dx \n\
int $0x10"
:
:
:"ax","cx","dx"
);
/* Reset video mode: */
__asm__ __volatile__ (
"movb $0x0, %%ah \n\
movb $0x03, %%al \n\
int $0x10"
:
:
:"ax"
);
return 0;
}
But the optional fields are only really useful if you're writing functions in assembly language and want to pass in arguments from your C code.
Also, I don't have DJGPP and a DOS installation handy, so I can't test any of this code to make sure it works with the 32-bit protected mode binaries it generates, but hopefully I've hit the nail close enough on the head that you can handle the rest yourself. Good luck!
Related
Using i686-elf-gcc and i686-elf-ld to compile and link.
/tmp/ccyjfCee.s:25: Error: invalid instruction suffix for 'mov'
makefile:21: recipe for target 'Release/boot.o' failed
When I tried to modify movw %0, %%dx to movw $0x1, %%dx. It compiled and linked successfully. So I wonder why there is something wrong with the line. In light of .code16, the offset address of pStr should be 16bit, which fits into dx register well. What's wrong with it?
__asm__(".code16\n");
void printString(const char* pStr) {
__asm__ __volatile__ ("movb $0x09, %%ah\n\t"
"movw %0, %%dx\n\t"
"int $0x21"
:
:"r"(pStr)
:"%ah", "%dx");
}
void _start() {
printString("Hello, World");
}
Technically you can use the .code16gcc directive to generate 16 bit code and the %w0 substitution to force word sized register.
Note that the above will only let you create a program that will run in 16 bit real mode under DOS (after some postprocessing to get it to the proper format). If that's not what you want, you will need to use the appropriate OS system calls instead of int 0x21 and not write 16 bit code.
I am writing Inline assembly for the first time and I don't know why I'm getting a Seg fault when I try to run it.
#include <stdio.h>
int very_fast_function(int i){
asm volatile("movl %%eax,%%ebx;"
"sall $6,%%ebx;"
"addl $1,%%ebx;"
"cmpl $1024,%%ebx;"
"jle Return;"
"addl $1,%%eax;"
"jmp End;"
"Return: movl $0,%%eax;"
"End: ret;": "=eax" (i) : "eax" (i) : "eax", "ebx" );
return i;
/*if ( (i*64 +1) > 1024) return ++i;
else return 0;*/
}
int main(int argc, char *argv[])
{
int i;
i=40;
printf("The function value of i is %d\n", very_fast_function(i));
return 0;
}
Like I said this is my first time so if it's super obvious I apologize.
You shall not use ret directly. Reason: there're initialization like push the stack or save the frame pointer when entering each function, also there're corresponding finalization. You just leave the stack not restored if use ret directly.
Just remove ret and there shall not be segmentation fault.
However I suppose the result is not as expected. The reason is your input/output constrains are not as expected. Please notice "=eax" (i) you write does not specify to use %%eax as the output of i, while it means to apply constraint e a and x on output variable i.
For your purpose you could simply use r to specify a register. See this edited code which I've just tested:
asm volatile("movl %1,%%ebx;"
"sall $6,%%ebx;"
"addl $1,%%ebx;"
"cmpl $1024,%%ebx;"
"jle Return;"
"addl $1,%0;"
"jmp End;"
"Return: movl $0,%0;"
"End: ;": "=r" (i) : "r" (i) : "ebx" );
Here To use %%eax explicitly, use "=a" instead of "=r".
For further information, please read this http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html
ret should not be used in inline assembly blocks - the function you're in needs some cleanup beyond what a simple ret will handle.
Remember, inline assembly is inserted directly into the function it's embedded in. It's not a function unto itself.
I am creating simple calculator application on bootloader using mixed code including C language with Assembly Code.
My C language Code is (addasm.c):
#include
int main() {
bootmain();
return 0 ;
}
int bootmain()
{
int arg1, arg2, add, sub, mul, quo, rem ;
printf( "Enter two integer numbers : " );
scanf( "%d%d", &arg1, &arg2 );
/* Perform Addition, Subtraction, Multiplication & Division */
__asm__ ( "addl %%ebx, %%eax;" : "=a" (add) : "a" (arg1) , "b" (arg2) );
__asm__ ( "subl %%ebx, %%eax;" : "=a" (sub) : "a" (arg1) , "b" (arg2) );
__asm__ ( "imull %%ebx, %%eax;" : "=a" (mul) : "a" (arg1) , "b" (arg2) );
__asm__ ( "movl $0x0, %%edx;"
"movl %2, %%eax;"
"movl %3, %%ebx;"
"idivl %%ebx;" : "=a" (quo), "=d" (rem) : "g" (arg1), "g" (arg2) );
printf( "%d + %d = %d\n", arg1, arg2, add );
printf( "%d - %d = %d\n", arg1, arg2, sub );
printf( "%d * %d = %d\n", arg1, arg2, mul );
printf( "%d / %d = %d\n", arg1, arg2, quo );
printf( "%d %% %d = %d\n", arg1, arg2, rem );
return 0;
}
I had created bootmain() function in C which i need to use in assembly code.
My assembly code (ccode.asm) is :
[BITS 16] ; 16 bit code generation
[ORG 0x7C00] ; ORGin location is 7C00
extern bootmain
;Main program
main: ; Main program label
call bootmain
; End matter
times 510-($-$$) db 0 ; Fill the rest of the sector with zeros
dw 0xAA55 ; Boot signature
Now i am compiling this
nasm -f elf -o main.o ccode.asm #assemble our asm file
But It gives me error for ORG keyword that it is undefined keyword.
If i will remove this keyword then it will give me errorless output.
After removing ORG keyword I am compiling like this way:
nasm -f elf -o main.o ccode.asm #assemble our asm file
gcc addasm.c main.o -o add_asm #compile and link in one step
./add_asm
So I am using this final add_asm file and make my usb drive bootable by puting this add_asm file using Disk Explorer.
But at booting it is showing message : Missing Operating System
So is this a problem of not using ORG in Assembly file.
This is mainly problem with ELF that i am using with NASM.But for external C function and for EXTERN keyword I need to use ELF.
The alternative code of ORG is :
[Bits 16]
extern bootmain
start:
mov ax, 07C0h ; Set up 4K stack space after this bootloader
add ax, 288 ; (4096 + 512) / 16 bytes per paragraph
mov ss, ax
mov sp, 4096
call bootmain
mov ax, 07C0h ; Set data segment to where we're loaded
mov ds, ax
times 510-($-$$) db 0; Pad remainder of boot sector with 0s
dw 0xAA55 ; The standard PC boot signature
But it also does not work... It gives me same error as "Missing Operating System" at time of booting.
Is there any another way to include C function in assembly file (*.asm)?
I am stuck here. If there is any suggestion please give me.
Thank you.
You cannot turn a normal C program into a bootloader like that.
The environment in which a bootloader runs is significantly different from a normal executable. In particular, it does not include a C library that you can link against (or, for that matter, any linker at all!), so functions like printf() and scanf() are not available unless you link in appropriate versions, which you are not doing.
You are compiling your program as a 32-bit executable. An x86 system boots up in 16-bit mode. There is a significant amount of initialization which must take place to switch into that mode, none of which is present here.
You are compiling your program as a Linux ELF (or possibly Windows PE?) executable. This is not the correct format for a bootloader.
I've been looking around a lot for examples of using inline ASM and I've seen seen a few different approaches.
I've gone with the -masm=intel option when compiling. As I understand it, when using this option you can just write the inline ASM as you would with intel syntax.
I have also seen approaches where people use ".intel_syntax"
When I compile I get the following message.
i586-mingw32msvc-gcc -masm=intel -o KDOS.exe KDOS.c
/tmp/ccVIXhRF.o:KDOS.c:(.text+0x5f): undefined reference to `address'
/tmp/ccVIXhRF.o:KDOS.c:(.text+0x6a): undefined reference to `ipAddr'
/tmp/ccVIXhRF.o:KDOS.c:(.text+0x79): undefined reference to `csAddr'
/tmp/ccVIXhRF.o:KDOS.c:(.text+0x11d): undefined reference to `address'
collect2: ld returned 1 exit status
I've looked around for a solution but I can't seem to find one. I've seen threads saying you can't pass C variables into inline ASM, but I've also seen some stuff saying there are workarounds. They didn't quite apply to what I was doing though so I wasn't really sure what to make of them. Sorry if it is an obvious answer but this is my first time using inline ASM much less fooling around with converting the syntax.
Here is my code. I am working through a book and this is some sample code within it. It was not compiled with gcc in the book so this is why I need to convert to intel syntax, because I need it to run on windows obviously. This is my modified version of the code:
// KDOS.c
// Chapter 2
#include<stdio.h>
#define WORD unsigned short
#define IDT_001_ADDR 0 //start address of first IVT vector
#define IDT_255_ADDR 1020 //start address of last IVT vector
#define IDT_VECTOR_SZ 4 //size of each IVT Vector (in bytes)
#define BP __asm{ int 0x3 } //break point
void main()
{
WORD csAddr; //Code segment of given interrupt
WORD ipAddr; //Starting IP for given interrupt
short address; //address in memory (0-1020)
WORD vector; //IVT entry ID (i.e., 0..255)
char dummy; //strictly to help pause program execution
vector = 0x0;
printf("\n---Dumping IVT from bottom up---\n");
printf("Vector\tAddress\t\n");
for
(
address=IDT_001_ADDR;
address<=IDT_255_ADDR;
address=address+IDT_VECTOR_SZ,vector++
)
{
printf("%03d\t%08p\t",vector,address);
//IVT starts at bottom of memory, so CS is alway 0x0
__asm__
(
".intel_syntax;"
"PUSH ES;"
"MOV AX, 0;"
"MOV ES,AX;"
"MOV BX,address;"
"MOV AX,ES:[BX];"
"MOV ipAddr,AX;"
"INC BX;"
"INC BX;"
"MOV AX,ES:[BX];"
"MOV csAddr,AX;"
"POP ES;"
);
printf("[CS:IP]=[%04X,%04X]\n",csAddr,ipAddr);
}
printf("press [ENTER] key to continue:");
scanf("%c",&dummy);
printf("\n---Overwrite IVT from top down---\n");
/*
Program will die somwhere around 0x4*
Note: can get same results via DOS debug.exe -e command
*/
for
(
address=IDT_255_ADDR;
address>=IDT_001_ADDR;
address=address-IDT_VECTOR_SZ,vector--
)
{
printf("Nulling %03d\t%08p\n",vector,address);
__asm__
(
".intel_syntax;"
"PUSH ES;"
"MOV AX,0;"
"MOV ES,AX;"
"MOV BX,address;"
"MOV ES:[BX],AX;"
"INC BX;"
"INC BX;"
"MOV ES:[BX],AX;"
"POP ES;"
);
}
return;
}/*end main()------------------------------------------------------------*/
Any help would be greatly appreciated. Once again my apologies if it is something obvious.
Actually you can pass C arguments to inline asm. But You have to define it after the asm code part.
In Your case something like this could work (You should add -masm=intel to the gcc's command line):
asm(
".intel_syntax noprefix;\n\t"
...
"MOV BX,%[address];\n\t"
...
".intel_syntax prefix;\n\t"
:: [address] "m" address, ...
: "AX", "BX", /* all changed registers to inform compiler to save them if needed */
);
See examples in a similar question.
Somebody over at SO posted a question asking how he could "hide" a function. This was my answer:
#include <stdio.h>
#include <stdlib.h>
int encrypt(void)
{
char *text="Hello World";
asm("push text");
asm("call printf");
return 0;
}
int main(int argc, char *argv[])
{
volatile unsigned char *i=encrypt;
while(*i!=0x00)
*i++^=0xBE;
return EXIT_SUCCESS;
}
but, there are problems:
encode.c: In function `main':
encode.c:13: warning: initialization from incompatible pointer type
C:\DOCUME~1\Aviral\LOCALS~1\Temp/ccYaOZhn.o:encode.c:(.text+0xf): undefined reference to `text'
C:\DOCUME~1\Aviral\LOCALS~1\Temp/ccYaOZhn.o:encode.c:(.text+0x14): undefined reference to `printf'
collect2: ld returned 1 exit status
My first question is why is the inline assembly failing ... what would be the right way to do it? Other thing -- the code for "ret" or "retn" is 0x00 , right... my code xor's stuff until it reaches a return ... so why is it SEGFAULTing?
As a high level point, I'm not quite sure why you're trying to use inline assembly to do a simple call into printf, as all you've done is create an incorrect version of a function call (your inline pushes something onto the stack, but never pop it off, most likely causing problems cause GCC isn't aware that you've modified the stack pointer in the middle of the function. This is fine in a trivial example, but could lead to non-obvious errors in a more complicated function)
Here's a correct implementation of your top function:
int encrypt(void)
{
char *text="Hello World";
char *formatString = "%s\n";
// volatile really isn't necessary but I just use it by habit
asm volatile("pushl %0;\n\t"
"pushl %1;\n\t"
"call printf;\n\t"
"addl $0x8, %%esp\n\t"
:
: "r"(text), "r"(formatString)
);
return 0;
}
As for your last question, the usual opcode for RET is "C3", but there are many variations, have a look at http://pdos.csail.mit.edu/6.828/2009/readings/i386/RET.htm
Your idea of searching for RET is also faulty as due to the fact that when you see the byte 0xC3 in a random set of instructions, it does NOT mean you've encountered a ret. As the 0xC3 may simply be the data/attributes of another instruction (as a side note, it's particularly hard to try and parse x86 instructions as you're doing due to the fact x86 is a CISC architecture with instruction lengths between 1-16 bytes)
As another note, not all OS's allow modification to the text/code segment (Where executable instructions are stored), so the the code you have in main may not work regardless.
GCC inline asm uses AT&T syntax (if no specific options are selected for using Intel's one).
Here's an example:
int a=10, b;
asm ("movl %1, %%eax;
movl %%eax, %0;"
:"=r"(b) /* output */
:"r"(a) /* input */
:"%eax" /* clobbered register */
);
Thus, your problem is that "text" is not identifiable from your call (and following instruction too).
See here for reference.
Moreover your code is not portable between 32 and 64 bit environments. Compile it with -m32 flag to ensure proper analysis (GCC will complain anyway if you fall in error).
A complete solution to your problem is on this post on GCC Mailing list.
Here's a snippet:
for ( i = method->args_size - 1; i >= 0; i-- ) {
asm( "pushl %0": /* no outputs */: \
"g" (stack_frame->op_stack[i]) );
}
asm( "call *%0" : /* no outputs */ : "g" (fp) :
"%eax", "%ecx", "%edx", "%cc", "memory" );
asm ( "movl %%eax, %0" : "=g" (ret_value) : /* No inputs */ );
On windows systems there's also an additional asm ( "addl %0, %%esp" : /* No outputs */ : "g" (method->args_size * 4) ); to do. Google for better details.
It is not printf but _printf