OSDev.org
https://forum.osdev.org/

Calling an elf object file from Assembly
https://forum.osdev.org/viewtopic.php?f=1&t=33662
Page 1 of 1

Author:  Optimizer [ Thu Apr 25, 2019 2:07 pm ]
Post subject:  Calling an elf object file from Assembly

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:
print msg ; this pseudo not my actual print code


bootsect.asm :
Code:
[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

Author:  nullplan [ Thu Apr 25, 2019 2:17 pm ]
Post subject:  Re: Calling an elf object file from Assembly

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
Code:
call dword [KERNEL_OFFSET + 24]

Does that work for you?

Author:  Optimizer [ Thu Apr 25, 2019 11:38 pm ]
Post subject:  Re: Calling an elf object file from Assembly

No sir it didn't work. Thank you for taking your time to help.

Author:  iansjack [ Fri Apr 26, 2019 1:26 am ]
Post subject:  Re: Calling an elf object file from Assembly

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.

Author:  Optimizer [ Fri Apr 26, 2019 2:07 am ]
Post subject:  Re: Calling an elf object file from Assembly

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.

Author:  bzt [ Fri Apr 26, 2019 1:03 pm ]
Post subject:  Re: Calling an elf object file from Assembly

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

Author:  MichaelPetch [ Fri Apr 26, 2019 2:45 pm ]
Post subject:  Re: Calling an elf object file from Assembly

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.

Author:  bzt [ Fri Apr 26, 2019 4:03 pm ]
Post subject:  Re: Calling an elf object file from Assembly

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

Author:  MichaelPetch [ Fri Apr 26, 2019 11:03 pm ]
Post subject:  Re: Calling an elf object file from Assembly

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.

Author:  Optimizer [ Fri Apr 26, 2019 11:53 pm ]
Post subject:  Re: Calling an elf object file from Assembly

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.

Author:  Optimizer [ Fri Apr 26, 2019 11:55 pm ]
Post subject:  Re: Calling an elf object file from Assembly

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.

Author:  bzt [ Sat Apr 27, 2019 5:09 am ]
Post subject:  Re: Calling an elf object file from Assembly

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

Author:  bigboyav [ Sun May 19, 2019 2:59 pm ]
Post subject:  Re: Calling an elf object file from Assembly

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.

Author:  ~ [ Mon May 20, 2019 7:19 am ]
Post subject:  Re: Calling an elf object file from Assembly

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:
#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;
}


Author:  MichaelPetch [ Mon May 20, 2019 8:41 am ]
Post subject:  Re: Calling an elf object file from Assembly

~ 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:
#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:
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

Page 1 of 1 All times are UTC - 6 hours
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
http://www.phpbb.com/