BOOT_DRIVE db 0 ; It is a good idea to store it in memory because 'dl' may get overwrittenMSG_REAL_MODE db "Started in 16-bit Real Mode", 0MSG_PROT_MODE db "Landed in 32-bit Protected Mode", 0MSG_LOAD_KERNEL db "Loading kernel into memory", 0

The entry point is not necessarily the first thing linked in. I'd drop the objcopy step and use the ELF file directly.

ELF files start with an ELF header (which isn't executable). The ELF header contains the entry point address. If you have loaded the file to the correct address, you should be able to just load the entry point address out of the header and jump there. The entry point address is 24 bytes into the file, and is one machine word in size. So, assuming ELF32, you could replace the final call instruction with

The most educational way to solve this problem would be to single-step through the code in a debugger (e.g. Bochs, SimNow or qemu + gdb). Inspect the registers and memory at each stage. Pay particular attention to the location and contents of your GDT, and the destination of function calls. It should then become fairly obvious where the error is.

Debugging techniques like this, practised on trivial code, will serve you well in the future when you meet more challenging problems.

I used objdump to examine kernel.o and noticed somehow kernel_entry.asm is not being put in there.

I removed the kernel_entry.asm completely and added an inline assembly in kernel.c to what kernel_entry.asm did and now everything is working fine.

If I were you, I wouldn't let this go, because it's a symptom of a bug in your build environment which will cause more troubles in the future.

Other than that, I agree with the others: iansjack is right you should use a debugger, and nullplan is also right that you should interpret the unmodified ELF object.

For the latter, I can offer example code how to parse and ELF. * In Assembly, for BIOS machines (uses protected mode, fasm syntax) * In C, for UEFI machinesNote that my code expects the ELF to be linked at -2M. If you want to load any arbitrary ELF kernel, then you must load (or copy) the kernel's segments to the specified locations (using program header's p_vaddr fields).

Given that he's using MinGW the default is probably the generation of i386pe instead of ELF which would have to be considered. I'd like to see his complete kernel_entry.asm code though.

What makes you think he's generating PE instead of ELF? He did not mentioned MinGW, but the subject of this topic is "Calling an elf object file from Assembly" and the OP starts with the sentence "I'm trying to call an ELF from assembly". The objdump output would tell if it's ELF or unintentionally PE. So yeah, I agree we need more info on what he is doing, kernel_entry.asm included.

To the OP:Just in case if it's PE what you meant, not ELF, then the steps are the same:1. parse the header, get the segment's offsets and sizes (text, data, bss)2. load (or copy) them into place3. and transfer control to the entry point.The only difference is you'll have to use different structs.

I learned what he was using when I looked at an os-image.bin file he generated and placed in his Github project. Build information was present in the last section (including compiler). His makefile was the first indication he was using a GCC on Windows.

Given that he's using MinGW the default is probably the generation of i386pe instead of ELF which would have to be considered. I'd like to see his complete kernel_entry.asm code though.

What makes you think he's generating PE instead of ELF? He did not mentioned MinGW, but the subject of this topic is "Calling an elf object file from Assembly" and the OP starts with the sentence "I'm trying to call an ELF from assembly". The objdump output would tell if it's ELF or unintentionally PE. So yeah, I agree we need more info on what he is doing, kernel_entry.asm included.

To the OP:Just in case if it's PE what you meant, not ELF, then the steps are the same:1. parse the header, get the segment's offsets and sizes (text, data, bss)2. load (or copy) them into place3. and transfer control to the entry point.The only difference is you'll have to use different structs.

I learned what he was using when I looked at an os-image.bin file he generated and placed in his Github project. Build information was present in the last section (including compiler). His makefile was the first indication he was using a GCC on Windows.

I learned what he was using when I looked at an os-image.bin file he generated and placed in his Github project. Build information was present in the last section (including compiler). His makefile was the first indication he was using a GCC on Windows.

I see. Then accidentally creating PEs could be an issue indeed, you made a good point!

Must have been an optimization issue, in my experience when the compiler optimizes a block of code out (even when I'm calling it), converting the block to inline assembly typically prevents the optimization.

Must have been an optimization issue, in my experience when the compiler optimizes a block of code out (even when I'm calling it), converting the block to inline assembly typically prevents the optimization.

This should return 1 since the string "here" is in the string to search. Compile and run without optimizations:

Code:

gcc test.c -O0 -m32 -Wall./a.out; echo $?1

Good to go! Not quite, build with optimizations on:

Code:

gcc test.c -O3 -m32 -Wall./a.out; echo $?0

So why did optimizations on cause this to fail? It is a subtle bug in the inline assembly. Passing pointers to memory through registers as is done with the constraints "=&c" (d0), "=&S" (d1) doesn't actually tell the compiler that what those pointers point at is actually going to be read or written. In this case the code generator produced code that never put the strings search and string on the stack as the optimizer never realized that the data in the character arrays were being accessed. We only told the compiler we were using the pointers (not what they point at). To get around this you can add a memory clobber to the inline assembly to ensure all the data in the arrays are saved (and then restored if need be) before the inline assembly is executed. Adding a memory clobber can be done with this modification:

Code:

:"dx", "di", "memory");

. This is discussed in the GCC inline assembly documentation along with an alternate solution (example shows a proper repne scasb) in the section 6.47.2.6 Clobbers and Scratch Registers

Who is online

Users browsing this forum: Bing [Bot] and 15 guests

You cannot post new topics in this forumYou cannot reply to topics in this forumYou cannot edit your posts in this forumYou cannot delete your posts in this forumYou cannot post attachments in this forum