I have got simple loader written in Nasm for i386
bits 32
section .text
align 4
dd 0x1BADB002
dd 0x00
dd - (0x1BADB002 + 0x00)
int 16
extern _run
global _start
global start
_start:
start:
call _run
ret
I compile it with nasm -f elf32 -o xyz.o xyz.asm, compile C files with gcc -m32 -o abc.o abc.c and link them with ld -m elf_i386 -o kernel.bin xyz.o abc.o. Everything is working well when I run it in qemu-system-i386 -m 64 -vga cirrus -kernel kernel.bin, but how can I port it to 64bit machines?
I can compile all the files with nasm -f elf64 -o xyz.o xyz.asm and gcc -m64 -o abc.o abc.c + link them, but I just get black screen on qemu-system-x86_64.
PS: Modifying bits 32 to bits 64 and seting align to 8 does not help.
Related
I'm tried this below tools, to call 2 files
My idea was to execute the function from c using assembler and create a bin file
nasm -f elf32 boot3.s -o boot3.o // convert asm file to object
gcc -c bootloader3.c -o bootloader3.o // convert c file to object
gcc boot3.o bootloader3.o -o boot3.bin // make bin file
boot3.s
section .text
bits 16
global _start ; Declare the entry point symbol
extern main ; Declare the main function as external
_start:
cli ; Disable the interrupts
call main ; Call the main function
hlt ; Halt the CPU
bootloader3.c
#include <stdio.h>
void main() {
printf("Hello, World!\n");
return;
}
My error: Error Image
Update 05.01.23
I tried to use a similar method, but with other commands, unfortunately another attempt also failed.
boot.s
bits 16
extern main
start:
cli ; Disable the interrupts
mov si, msg ; SI now points to our message
mov ah, 0x0E ; Indicate BIOS we're going to print chars
.loop lodsb ; Loads SI into AL and increments SI [next char]
or al, al ; Checks if the end of the string
jz halt ; Jump to halt if the end
int 0x10 ; Otherwise, call interrupt for printing the char
jmp .loop ; Next iteration of the loop
halt: hlt ; CPU command to halt the execution
msg: db "Hello, World!", 0 ; Our actual message to print
section .text
global puts
puts:
; Insert code for the puts function here
times 510 - ($ - $$) db 0
; Add the boot signature
dw 0xAA55
bootloader.c
//extern void puts(const char* str);
int main(void)
{
puts("Hello user");
return 0;
}
Compile using tools
First Step
nasm -f elf64 boot.s -o boot.o
gcc -c -m32 -o bootloader.o bootloader.c
ld -Ttext=0x7C00 -o boot.elf boot.o bootloader.o
Second Step
nasm -f bin boot.s -o boot.o
gcc -c -m32 -o bootloader.o bootloader.c
gcc -m32 -nostdlib -nostartfiles -Wl,-Ttext,0x7C00 boot.o bootloader.c -o boot.elf
Third Step
nasm -f elf32 boot.s -o boot.o
gcc -c -m32 -o bootloader.o bootloader.c
ld -Ttext=0x7C00 -o boot.elf boot.o bootloader.o
My Error:
On First & Third step
ld: bootloader.o:bootloader.c:(.text+0xa): undefined reference to `__main' ld: bootloader.o:bootloader.c:(.text+0x16): undefined reference to `puts'
On Seconds step
c:/mingw/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/bin/ld.exe: C:\Users\Klubuntu\AppData\Local\Temp\ccojaMyU.o:bootloader.c:(.text+0xa): undefined reference to `__main' c:/mingw/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/bin/ld.exe: C:\Users\Klubuntu\AppData\Local\Temp\ccojaMyU.o:bootloader.c:(.text+0x16): undefined reference to `puts' collect2.exe: error: ld returned 1 exit status
Sorry for all grammar mistakes
Thanks from above help
Remote debugging a code running in Qemu with GDB, based on an os-dev tutorial.
My version is here. The problem only happens when remote-debugging code inside qemu, not when building a normal executable to run directly inside GDB under the normal OS.
Code looks something like this:
#define BUFSIZE 255
static char buf[BUFSIZE];
void foo() {
// Making sure it's all zero.
for (int i = 0; i < BUFSIZE; i++) buf[i] = 0;
// Setting first char:
buf[0] = 'a';
// >> insert breakpoint right after setting the char <<
// Prints 'a'.
printf("%s", buf);
}
If I place a breakpoint at the marked spot and print the buffer with p buf I get random values from random places, seemingly from my code section. If I get the address by p &buf I get something that does not look correct, for two things:
If I do a char* p_buf = buf and I check the address with p p_buf it gives me a totally different address, which is stable across executions (the other was not). Then I inspect that memory section with x /255b 0x____ and I can see the a and then zeros (97 0 0 0 ... 0).
The next command (printf("%s", buf);) does actually prints a.
This leaves me believing it might be GDB not knowing the correct location if I only inspect the static variable.
Where should I start debugging this?
Details about the compile conditions:
Compile flags: -g -Wall -Wextra -pedantic -nostdlib -nostdinc -fno-builtin -fno-stack-protector -nostartfiles -nodefaultlibs -m32
qemu-system-i386
Gcc: i386 elf target
Example output from GDB:
(gdb) p buf
$1 = "dfghjkl;'`\000\\zxcvbnm,./\000*\000 ", '\000' <repeats 198 times>...
(gdb) p p_buf
$2 = 0x40c0 <buf+224> "a"
(gdb) p &buf
$3 = (char (*)[255]) 0x3fe0 <buf>
(gdb) info address buf
Symbol "buf" is static storage at address 0x3fe0.
Update 2:
Disassembled a version of the code that shows the discrepancy:
; void foo
0x19f1 <foo> push %ebp
0x19f2 <foo+1> mov %esp,%ebp
0x19f4 <foo+3> sub $0x10,%esp
; char* p_buf = char_buf; --> `p &char_buf` is 0x4040 (incorrect) but `p p_buf` is 0x4100
0x19f7 <foo+6> movl $0x4100,-0x4(%ebp)
; void* p_p_buf = (void*)p_buf; --> `p p_p_buf` gives 0x4100
0x19fe <foo+13> mov -0x4(%ebp),%eax
0x1a01 <foo+16> mov %eax,-0x8(%ebp)
; void* p_char_buf = (void*)&char_buf; --> `p p_char_buf` gives 0x4100
0x1a04 <foo+19> movl $0x4100,-0xc(%ebp)
; char_buf[0] = 'a'; --> correct address
0x1a0b <foo+26> movb $0x61,0x4100
; char_buf[1] = 'b'; --> correct address (asking `p &char_buf` here is still incorrectly 0x4040)
0x1a12 <foo+33> movb $0x62,0x4101
; void foo return
0x1a19 <foo+40> nop
0x1a1a <foo+41> leave
0x1a1b <foo+42> ret
My Makefile for building the project looks like:
C_SOURCES = $(wildcard kernel/*.c drivers/*.c)
C_HEADERS = $(wildcard kernel/*.h drivers/*.h)
OBJ = ${C_SOURCES:.c=.o kernel/interrupt_table.o}
CC = /home/itarato/code/os/i386elfgcc/bin/i386-elf-gcc
# GDB = /home/itarato/code/os/i386elfgcc/bin/i386-elf-gdb
GDB = /usr/bin/gdb
CFLAGS = -g -Wall -Wextra -ffreestanding -fno-exceptions -pedantic -fno-builtin -fno-stack-protector -nostartfiles -nodefaultlibs -m32
QEMU = qemu-system-i386
os-image.bin: boot/boot.bin kernel.bin
cat $^ > $#
kernel.bin: boot/kernel_entry.o ${OBJ}
i386-elf-ld -o $# -Ttext 0x1000 $^ --oformat binary
kernel.elf: boot/kernel_entry.o ${OBJ}
i386-elf-ld -o $# -Ttext 0x1000 $^
kernel.dis: kernel.bin
ndisasm -b 32 $< > $#
run: os-image.bin
${QEMU} -drive format=raw,media=disk,file=$<,index=0,if=floppy
debug: os-image.bin kernel.elf
${QEMU} -s -S -drive format=raw,media=disk,file=$<,index=0,if=floppy &
${GDB} -ex "target remote localhost:1234" -ex "symbol-file kernel.elf" -ex "tui enable" -ex "layout split" -ex "focus cmd"
%.o: %.c ${C_HEADERS}
${CC} ${CFLAGS} -c $< -o $#
%.o: %.asm
nasm $< -f elf -o $#
%.bin: %.asm
nasm $< -f bin -o $#
build: os-image.bin
echo Pass
clean:
rm -rf *.bin *.o *.dis *.elf
rm -rf kernel/*.o boot/*.bin boot/*.o
For me, this doesn't seem to happen:
Breakpoint 1, main () at test65.c:16
16 printf("%s", buf);
(gdb) p buf
$2 = "a", '\000' <repeats 253 times>
Where should I start debugging this?
It seems like there are two things that might go wrong:
1. GDB might be reading from wrong location
I'm not sure what could cause this, but it is easy enough to verify. Check what address p &buf gives you. Then compare it to what you get from p_buf and also to what info address buf shows you.
Note that due to address space layout randomization the address of static variables will change at the point when you start the process. So before run command the address could be e.g. 0x4040 and then change to 0x555555558040 once the code is running:
(gdb) info address buf
Symbol "buf" is static storage at address 0x4040.
(gdb) run
....
Breakpoint 1, main () at test65.c:16
16 printf("%s", buf);
(gdb) p &buf
$1 = (char (*)[255]) 0x555555558040 <buf>
(gdb) info address buf
Symbol "buf" is static storage at address 0x555555558040.
2. GDB is reading correct place, but data is not there yet
It sounds like a typical debugging problem caused by compiler optimizations. For example, the compiler might move the setting of buf[0] = a after the point where your breakpoint lands, though it must set it before printf() gets called. You could try compiling with -O0 to see if it changes anything.
You can also check the disassembly with disas command, to see what has executed up to that point:
(gdb) disas
Dump of assembler code for function main:
0x000055555555517b <+50>: movb $0x61,0x2ebe(%rip) # 0x555555558040 <buf>
=> 0x0000555555555182 <+57>: lea 0x2eb7(%rip),%rsi # 0x555555558040 <buf>
0x0000555555555189 <+64>: lea 0xe74(%rip),%rdi # 0x555555556004
0x0000555555555190 <+71>: mov $0x0,%eax
0x0000555555555195 <+76>: callq 0x555555555050 <printf#plt>
For me the breakpoint lands at the point right after movb sets 0x61 (letter a) to buf.
If you use stepi command until you are at callq printf instruction, you can be sure you see the buffer exactly like printf would see it.
This is an interesting problem. It comes down to the fact that the code generated by LD (linker) for the ELF executable kernel.elf is different from that of the code generated by LD for kernel.bin when using the --oformat binary option. While one would expect these to be the same, they are not.
More simply put these Makefile rules do not produce the same code as you might expect:
kernel.elf: boot/kernel_entry.o ${OBJ}
i386-elf-ld -o $# -Ttext 0x1000 $^
and
kernel.bin: boot/kernel_entry.o ${OBJ}
i386-elf-ld -o $# -Ttext 0x1000 $^ --oformat binary
It appears the difference is in how the linker is aligning the sections when used with and without --oformat binary. The ELF file (and the symbols used for debugging) are seen to be in one place while the binary file that is actually running in QEMU had code and data generated at different offsets.
I hadn't ever observed this issue because I use my own linker scripts and I always generate the binary file from the ELF executable with OBJCOPY rather than using LD to link twice. OBJCOPY can take an ELF executable and convert it to a binary file. The Makefile rules could be amended to look like:
kernel.bin: kernel.elf
i386-elf-objcopy -O binary $^ $#
kernel.elf: boot/kernel_entry.o ${OBJ}
i386-elf-ld -o $# -Ttext 0x1000 $^
Doing it this way will ensure the binary file that is generated matches what was produced for the ELF executable.
I'm working my way through The Little Book About OS Development, specifically the section on the framebuffer (linked). I'm able to successfully assemble, link, turn into an ISO file and boot pure assembly, but as soon as I try to link a compiled object file for my C code (called from my loader, which was written in assembly), the linker complains. Here's the output:
nasm -f elf loader.s -o loader.o
nasm -f elf out.s -o out.o
/usr/local/Cellar/gcc#6/6.4.0/bin/gcc-6 -m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector -nostartfiles -nodefaultlibs -Wall -Wextra -Werror -c framebuffer.c -o framebuffer.o
/usr/local/Cellar/gcc#6/6.4.0/bin/gcc-6 -m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector -nostartfiles -nodefaultlibs -Wall -Wextra -Werror -c kmain.c -o kmain.o
i386-unknown-linux-gnu-ld -T link.ld -melf_i386 loader.o out.o framebuffer.o kmain.o -o kernel.elf
framebuffer.o: file not recognized: File format not recognized
make: *** [kernel.elf] Error 1
I'm on a Mac, so as you can tell I've compiled a custom version of the GNU linker so that I can use linker scripts, and I've made sure to specify GCC 6 (the system default is 4.0). Anyway, here's my Makefile:
OBJECTS = loader.o out.o framebuffer.o kmain.o
CC = /usr/local/Cellar/gcc#6/6.4.0/bin/gcc-6
CFLAGS = -m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector \
-nostartfiles -nodefaultlibs -Wall -Wextra -Werror -c
LDFLAGS = -T link.ld -melf_i386
AS = nasm
ASFLAGS = -f elf
all: kernel.elf
kernel.elf: $(OBJECTS)
i386-unknown-linux-gnu-ld $(LDFLAGS) $(OBJECTS) -o kernel.elf
os.iso: kernel.elf
cp kernel.elf iso/boot/kernel.elf
mkisofs -R \
-b boot/grub/stage2_eltorito \
-no-emul-boot \
-boot-load-size 4 \
-A os \
-input-charset utf8 \
-quiet \
-boot-info-table \
-o os.iso \
iso
run: os.iso
bochs -f bochsrc.txt -q
%.o: %.c
$(CC) $(CFLAGS) $< -o $#
%.o: %.s
$(AS) $(ASFLAGS) $< -o $#
clean:
rm -rf *.o kernel.elf os.iso
Here's my linker script:
ENTRY(loader) /* the name of the entry label */
SECTIONS {
. = 0x00100000; /* the code should be loaded at 1 MB */
.text ALIGN (0x1000) : /* align at 4 KB */
{
*(.text) /* all text sections from all files */
}
.rodata ALIGN (0x1000) : /* align at 4 KB */
{
*(.rodata*) /* all read-only data sections from all files */
}
.data ALIGN (0x1000) : /* align at 4 KB */
{
*(.data) /* all data sections from all files */
}
.bss ALIGN (0x1000) : /* align at 4 KB */
{
*(COMMON) /* all COMMON sections from all files */
*(.bss) /* all bss sections from all files */
}
}
Any help would be appreciated.
As it turns out, this was solved by cross-compiling GNU Binutils and GCC together (with the same prefix) on my Mac. I did this according to the instructions here.
I'm trying to compile a bare-metal app with GCC compiler (Standard C). I use Cyclone V SoC with Cortex-A9 processor. Eclipse DS-5. I get these errors - "Region ram overflowed by 295376 bytes" and "section .text will not fit region ram". I think that the problem isn't in the linker script but in something else. I see messages that compiler tries to add all my .c files in project into one .axf file even if I include none of them in my main .c file (where I write the program) When I delete some unused .c files from project it says "Region ram overflowed by 275433 bytes" (different overflow size). What should I do to get rid of this mistake?
flash.ld
MEMORY
{
ram : ORIGIN = 0x00000000, LENGTH = 0x100
}
SECTIONS
{
.text : { *(.text*) } > ram
.rodata : { *(.rodata*) } > ram
.bss : { *(.bss*) } > ram
}
flash.s
.globl _start
_start:
b reset
b hang
b hang
b hang
b hang
b hang
b hang
b hang
reset:
mov sp,#0x8000
bl notmain
b hang
hang:
b hang
notmain.c
unsigned int data[1000];
int notmain ( void )
{
unsigned int ra;
for(ra=0;ra<1000;ra++) data[ra]=ra;
return(0);
}
Makefile
ARMGNU = arm-none-eabi
COPS = -O2 -nostdlib -nostartfiles -ffreestanding
all : notmain.bin
clean:
rm -f *.bin
rm -f *.o
rm -f *.elf
rm -f *.list
flash.o : flash.s
$(ARMGNU)-as $(AOPS) flash.s -o flash.o
notmain.o : notmain.c
$(ARMGNU)-gcc $(COPS) -c notmain.c -o notmain.o
notmain.bin : flash.ld flash.o notmain.o
$(ARMGNU)-ld -o notmain.elf -T flash.ld flash.o notmain.o
$(ARMGNU)-objdump -D notmain.elf > notmain.list
$(ARMGNU)-objcopy notmain.elf notmain.bin -O binary
output:
arm-none-eabi-ld -o notmain.elf -T flash.ld flash.o notmain.o
arm-none-eabi-ld:flash.ld:10: warning: memory region `rom' not declared
arm-none-eabi-ld: notmain.elf section `.bss' will not fit in region `ram'
arm-none-eabi-ld: region `ram' overflowed by 3828 bytes
Makefile:21: recipe for target 'notmain.bin' failed
make: *** [notmain.bin] Error 1
I could have made it say .text wont fit, but it is the same problem you are having. Change the size in the linker script.
ram : ORIGIN = 0x00000000, LENGTH = 0x1000
and now it is happy
arm-none-eabi-ld -o notmain.elf -T flash.ld flash.o notmain.o
arm-none-eabi-objdump -D notmain.elf > notmain.list
arm-none-eabi-objcopy notmain.elf notmain.bin -O binary
The .text section, which is your program itself basically, was too big for the "memory" allocated for it. If the linker script you are using reflects the true size of what you are allocated, your program is too big you need to make it smaller, can start by optimizing if you are not (-O2 on the gcc command line) or putting static in front of functions that are not global, or just overall reducing the amount of code by cleaning up. that doesnt mean make a few lines of C into one long line of C with no real functionality removed, you need to have it do fewer things.
Or as in my case here perhaps you have some .data or .bss or other items that are also in the same section defined in the linker script and the combination of all of them are taking up too much space. Changing the length to 0x10 in my example above it complains about .text first without the others, as above if I make it 0x100 it complains about .bss then stops complaining, so ld is complaining about the one that actively crosses the line not the ones that didnt get pulled in yet.
You can make the length larger to get it to build, then examine the elf file (objdump or readelf or whatever) and from there perhaps get an idea of what part is really too big, what functions are huge or what data, etc. Functions that are global that dont need to be that are being inlined by the optimizer, etc.
I'm trying to make a kernel, and I cannot link the C output with the assembly. The ld. I'm getting the error:
unrecognized emulation mode: elf_i386
I'm using Windows 10 professional with the MinGW32 and MSYS. The code I am using:
link.ld
/*
* link.ld
*/
OUTPUT_FORMAT(elf32-i386)
ENTRY(start)
SECTIONS
{
. = 0x100000;
.text : { *(.text) }
.data : { *(.data) }
.bss : { *(.bss) }
}
kernel.c
/*
* kernel.c
*/
void kmain(void)
{
const char *str = "my first kernel";
char *vidptr = (char*)0xb8000; //video mem begins here.
unsigned int i = 0;
unsigned int j = 0;
/* this loops clears the screen
* there are 25 lines each of 80 columns; each element takes 2 bytes */
while(j < 80 * 25 * 2) {
/* blank character */
vidptr[j] = ' ';
/* attribute-byte - light grey on black screen */
vidptr[j+1] = 0x07;
j = j + 2;
}
j = 0;
/* this loop writes the string to video memory */
while(str[j] != '\0') {
/* the character's ascii */
vidptr[i] = str[j];
/* attribute-byte: give character black bg and light grey fg */
vidptr[i+1] = 0x07;
++j;
i = i + 2;
}
return;
}
kernel.asm
;;kernel.asm
bits 32 ;nasm directive - 32 bit
section .text
global start
extern kmain ;kmain is defined in the c file
start:
cli ;block interrupts
mov esp, stack_space ;set stack pointer
call kmain
hlt ;halt the CPU
section .bss
resb 8192 ;8KB for stack
stack_space:
To Compile and link I use:
nasm -f elf32 kernel.asm -o kasm.o
gcc -m32 -c kernel.c -o kc.o
ld -m elf_i386 -T link.ld -o kernel kasm.o kc.o
I'm Using:
Gcc 4.8.1
Ld 2.25.1
Nasm 2.11.09rc1
Why am I getting this error, and how can I fix it?
The standard MinGW/32 LD linker doesn't output ELF binaries. Preferably you would be using an i686 cross-compiler, but if you're not you may be able to get away with the tips below.
It appears you are using Arjun's Let's Write a Kernel tutorial. If you are following that tutorial you have missed a step to make kernel.asm compatible with the GRUB boot loader and QEMU's -kernel option. Before we start you should read the rest of the tutorial. The following code adds a Multiboot header to kernel.asm to make it GRUB compatible:
;;kernel.asm
bits 32 ;nasm directive - 32 bit
global entry
extern _kmain ;kmain is defined in the c file
section .text
entry: jmp start
;multiboot spec
align 4
dd 0x1BADB002 ;magic
dd 0x00 ;flags
dd -(0x1BADB002 + 0x00) ;checksum. m+f+c should be zero
start:
cli ;block interrupts
mov esp, stack_space ;set stack pointer
call _kmain
hlt ;halt the CPU
section .bss
resb 8192 ;8KB for stack
stack_space:
Besides adding a header I've also put an entry label in the file and a jmp start to jump over the Multiboot header. I've done this to make it easy to set a breakpoint at 0x100000 in the future if you start debugging.
One other change is that on MinGW, GCC adds an underscore to function names by default. I've changed references to the C function kmain to _kmain. This differs from the Linux convention.
Since the entry point of our code is now entry instead of start I've modified link.ld to be:
/*
* link.ld
*/
OUTPUT_FORMAT(pei-i386)
ENTRY(entry)
SECTIONS
{
. = 0x100000;
.text : { *(.text) }
.data : { *(.data) }
.bss : { *(.bss) }
}
Another important change in the file above is the usage of OUTPUT_FORMAT(pei-i386) . This will output a Portable Executable Image (32-bit) rather than an ELF (which isn't supported).
In order to build the kernel and produce an ELF image from the PEI-I386 we can use these commands:
nasm -f elf32 kernel.asm -o kasm.o
gcc -m32 -c kernel.c -o kc.o -ffreestanding -nostdlib -nostdinc
ld -T link.ld -o kernel kasm.o kc.o -build-id=none
objcopy -O elf32-i386 kernel kernel.elf
The LD command has been modified to not write out the build-id to the executable to avoid the Multiboot header from being shifted outside the first 8k of the executable. The GCC options have been modified to produce freestanding code (without the standard library and includes) using the options -ffreestanding -nostdlib -nostdinc. We use objcopy to convert the PEI-I386 file (kernel) to an ELF32 image called kernel.elf. You will want to be using kernel.elf with GRUB and/or QEMU.