Page 1 of 1
Calling an elf object file from Assembly
Posted: Thu Apr 25, 2019 2:07 pm
by Optimizer
I'm trying to call an ELF from assembly
1)I created an assembly file with a simple print code and assembled it into an ELF .o file
2)I linked this file using ld to the my bootsect.asm file(with offset of 0x1000)
ld -o kernel.tmp - Ttext 0x1000 kernel_entry.o
objcopy - O binary kernel.tmp kernel.bin
then I combine bootsect.bin and kernel.bin to get my final image. I also tried - T NUL with ld
3)bootsect.asm uses BIOS interrupts to load the elf to memory(to 0x1000) and I call 0x1000
but the print function doesn't execute.
I'm following this tutorial :
https://github.com/cfenollosa/os-tutori ... -barebones
kernel_entry.asm :
Code: Select all
print msg ; this pseudo not my actual print code
bootsect.asm :
Code: Select all
[org 0x7c00]
KERNEL_OFFSET equ 0x1000 ; The same one we used when linking the kernel
mov [BOOT_DRIVE], dl ; Remember that the BIOS sets us the boot drive in 'dl' on boot
mov bp, 0x9000
mov sp, bp
mov bx, MSG_REAL_MODE
call print
call print_nl
call load_kernel ; read the kernel from disk
call switch_to_pm ; disable interrupts, load GDT, etc. Finally jumps to 'BEGIN_PM'
jmp $ ; Never executed
%include "../05-bootsector-functions-strings/boot_sect_print.asm"
%include "../05-bootsector-functions-strings/boot_sect_print_hex.asm"
%include "../07-bootsector-disk/boot_sect_disk.asm"
%include "../09-32bit-gdt/32bit-gdt.asm"
%include "../08-32bit-print/32bit-print.asm"
%include "../10-32bit-enter/32bit-switch.asm"
[bits 16]
load_kernel:
mov bx, MSG_LOAD_KERNEL
call print
call print_nl
mov bx, KERNEL_OFFSET ; Read from disk and store in 0x1000
mov dh, 2
mov dl, [BOOT_DRIVE]
call disk_load
ret
[bits 32]
BEGIN_PM:
mov ebx, MSG_PROT_MODE
call print_string_pm
call KERNEL_OFFSET ; Give control to the kernel
jmp $ ; Stay here when the kernel returns control to us (if ever)
BOOT_DRIVE db 0 ; It is a good idea to store it in memory because 'dl' may get overwritten
MSG_REAL_MODE db "Started in 16-bit Real Mode", 0
MSG_PROT_MODE db "Landed in 32-bit Protected Mode", 0
MSG_LOAD_KERNEL db "Loading kernel into memory", 0
; padding
times 510 - ($-$$) db 0
dw 0xaa55
disk_load label is here
https://github.com/cfenollosa/os-tutori ... t_disk.asm
Thanks
Re: Calling an elf object file from Assembly
Posted: Thu Apr 25, 2019 2:17 pm
by nullplan
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
Does that work for you?
Re: Calling an elf object file from Assembly
Posted: Thu Apr 25, 2019 11:38 pm
by Optimizer
No sir it didn't work. Thank you for taking your time to help.
Re: Calling an elf object file from Assembly
Posted: Fri Apr 26, 2019 1:26 am
by iansjack
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.
Re: Calling an elf object file from Assembly
Posted: Fri Apr 26, 2019 2:07 am
by Optimizer
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.
Re: Calling an elf object file from Assembly
Posted: Fri Apr 26, 2019 1:03 pm
by bzt
Hi,
Optimizer wrote: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 machines
Note 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).
Cheers,
bzt
Re: Calling an elf object file from Assembly
Posted: Fri Apr 26, 2019 2:45 pm
by MichaelPetch
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.
Re: Calling an elf object file from Assembly
Posted: Fri Apr 26, 2019 4:03 pm
by bzt
Hi,
MichaelPetch wrote: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 place
3. and transfer control to the entry point.
The only difference is you'll have to use different structs.
Cheers,
bzt
Re: Calling an elf object file from Assembly
Posted: Fri Apr 26, 2019 11:03 pm
by MichaelPetch
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.
Re: Calling an elf object file from Assembly
Posted: Fri Apr 26, 2019 11:53 pm
by Optimizer
bzt wrote:Hi,
MichaelPetch wrote: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 place
3. and transfer control to the entry point.
The only difference is you'll have to use different structs.
Cheers,
bzt
Yeah definitely shouldn't let this go. I will continue my os after fixing this.
Re: Calling an elf object file from Assembly
Posted: Fri Apr 26, 2019 11:55 pm
by Optimizer
MichaelPetch wrote: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.
You're right I'm using mingw but I will soon switch to linux.
Re: Calling an elf object file from Assembly
Posted: Sat Apr 27, 2019 5:09 am
by bzt
MichaelPetch wrote: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!
Cheers,
bzt
Re: Calling an elf object file from Assembly
Posted: Sun May 19, 2019 2:59 pm
by bigboyav
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.
Re: Calling an elf object file from Assembly
Posted: Mon May 20, 2019 7:19 am
by ~
bigboyav wrote: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.
In GCC, __asm__ __volatile__ leaves the assembly block untouched. A typical example is linux/arch/i386/lib/strstr.c:
strstr.c
Code: Select all
#include <linux/string.h>
char * strstr(const char * cs,const char * ct)
{
int d0, d1;
register char * __res;
__asm__ __volatile__(
"movl %6,%%edi\n\t"
"repne\n\t"
"scasb\n\t"
"notl %%ecx\n\t"
"decl %%ecx\n\t" /* NOTE! This also sets Z if searchstring='' */
"movl %%ecx,%%edx\n"
"1:\tmovl %6,%%edi\n\t"
"movl %%esi,%%eax\n\t"
"movl %%edx,%%ecx\n\t"
"repe\n\t"
"cmpsb\n\t"
"je 2f\n\t" /* also works for empty string, see above */
"xchgl %%eax,%%esi\n\t"
"incl %%esi\n\t"
"cmpb $0,-1(%%eax)\n\t"
"jne 1b\n\t"
"xorl %%eax,%%eax\n\t"
"2:"
:"=a" (__res), "=&c" (d0), "=&S" (d1)
:"0" (0), "1" (0xffffffff), "2" (cs), "g" (ct)
:"dx", "di");
return __res;
}
Re: Calling an elf object file from Assembly
Posted: Mon May 20, 2019 8:41 am
by MichaelPetch
~ wrote:A typical example is linux/arch/i386/lib/strstr.c:
Typical example (even from older Linux kernel) with a bug. To demonstrate try this code:
Code: Select all
#include <stdio.h>
char * strstr(const char * cs,const char * ct)
{
int d0, d1;
register char * __res;
__asm__ __volatile__(
"movl %6,%%edi\n\t"
"repne\n\t"
"scasb\n\t"
"notl %%ecx\n\t"
"decl %%ecx\n\t" /* NOTE! This also sets Z if searchstring='' */
"movl %%ecx,%%edx\n"
"1:\tmovl %6,%%edi\n\t"
"movl %%esi,%%eax\n\t"
"movl %%edx,%%ecx\n\t"
"repe\n\t"
"cmpsb\n\t"
"je 2f\n\t" /* also works for empty string, see above */
"xchgl %%eax,%%esi\n\t"
"incl %%esi\n\t"
"cmpb $0,-1(%%eax)\n\t"
"jne 1b\n\t"
"xorl %%eax,%%eax\n\t"
"2:"
:"=a" (__res), "=&c" (d0), "=&S" (d1)
:"0" (0), "1" (0xffffffff), "2" (cs), "g" (ct)
:"dx", "di");
return __res;
}
int main()
{
char string[]="Hello There";
char search[]="here";
return (strstr(string, search) ? 1 : 0);
}
This should return 1 since the string "here" is in the string to search. Compile and run without optimizations:
Code: Select all
gcc test.c -O0 -m32 -Wall
./a.out; echo $?
1
Good to go! Not quite, build with optimizations on:
Code: Select all
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:
. 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