Author Topic: trying to build raw binary with both gcc and nasm  (Read 10201 times)

Offline ggnasm800

  • New Member
  • Posts: 1
trying to build raw binary with both gcc and nasm
« on: December 28, 2020, 08:41:49 PM »
I am trying to build raw executable binary to load and execute at arbitrary memory address at boot.

For elf binary, i can easily generate 32-bit binary:

nasm -felf32 -F dwarf boota.asm
gcc -m32 bootc.c boota.o -o boot.elf.


However i dont need elf file along its headers. I also see when main() is declared (as program entry) compiler puts additional (possibly O/S dependent code) prior to executing main() so I dont need those too.

I sort of did hacked the resulting elf binary such that bootloader code will directly jump into main() but when main() calls one of the assembler function, it interprets call dest. address 2 bytes less.

How do i make it work?

in brief:
opcode on vm sees: e8 48 00 (call label at 6a) (using virtualbox vdebug capability to step-through)
opcode on objdump sees: (call label at 6c) e8 48 00 00 00

more code (code starts executing at 800:00 address after bios boot):
from virtual box debugger:

VBoxDbg> u 800:0
0800:0 8d 4c 24                       lea cx, [si+024h]
0800:03 04 83                         add AL, 083h
0800:05 e4 f0                         in AL, 0f0h
0800:07 ff 71 fc                      push word [bx+di-004h]
0800:0a 55                            push bp
0800:0b 89 e5                         mov bp, sp
0800:0d 53                            push bx
0800:0e 51                            push cx
0800:0f e8 ef fe                      call 0ff01h
0800:00000012 ff ff                   Illegal opcode
0800:00000014 81 c3 db 1a             add bx, 01adbh
0800:00000018 00 00                   add byte [bx+si], al
0800:0000001a 83 ec 0c                sub sp, byte 0000ch
0800:0000001d 6a 5b                   push byte 0005bh
0800:0000001f e8 48 00                call 0006ah  <--------------------------------- ( here it is calling the assembler defined function or label, but as you can see below, function entry is at 6c)
0800:00000022 00 00                   add byte [bx+si], al
0800:00000024 83 c4 10                add sp, byte 00010h
0800:00000027 83 ec 04                sub sp, byte 00004h
0800:0000002a 6a f9                   push byte 0fff9h
0800:0000002c 6a fc                   push byte 0fffch

0800:00000065 66 39 d0                cmp eax, edx
0800:00000068 66 0f 4c c2             cmovl eax, edx
0800:0000006c b4 0e                   mov AH, 00eh <---------- (but function is at 6c)
0800:0000006e b0 25                   mov AL, 025h
0800:00000070 cd 10

disassemly looks OK:

OBJDUMP OUTPUT:

Disassembly of section .data:

0 <.data>:
       0:       8d 4c 24 04             lea    0x4(%esp),%ecx
       4:       83 e4 f0                and    $0xf0,%esp
       7:       ff 71 fc                pushl  -0x4(%ecx)
       a:       55                      push   %ebp
       b:       89 e5                   mov    %esp,%ebp
       d:       53                      push   %ebx
       e:       51                      push   %ecx
       f:       e8 ef fe ff ff          call   0xffffff03
      14:       81 c3 db 1a 00 00       add    $0x1adb,%ebx
      1a:       83 ec 0c                sub    $0xc,%esp
      1d:       6a 5b                   push   $0x5b
      1f:       e8 48 00 00 00          call   0x6c

« Last Edit: December 28, 2020, 08:43:40 PM by ggnasm800 »

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: trying to build raw binary with both gcc and nasm
« Reply #1 on: January 01, 2021, 12:44:11 AM »
Hi ggnasm800,

Welcome to the forum.

I don't think I can help you much with this.

Telling GCC "--nostartfiles" will get rid pf the startup code that calls main.

Your virtual box debugger is trying to disassemble 16 bit code but it is 32 bit.

The int 10h is a bios interrupt - 16 bit code. We don't see if you have put the CPU into 32 bit mode or not. If so, the int 10h isn't going to work. If not, the rest of it isn't going to work.

You might do better to ask in an OS development group.

Best,
Frank





Offline fredericopissarra

  • Full Member
  • **
  • Posts: 373
  • Country: br
Re: trying to build raw binary with both gcc and nasm
« Reply #2 on: January 01, 2021, 11:48:30 AM »
Here's a possible example of how to do it: Let's say you have a 16 bits code and want to mix it with C code compiled with GCC. In this example I use 2 different text sections: One for my assembly code, one for C code. Here's the linker script:

Code: [Select]
/* linker.ld */
OUTPUT_FORMAT(binary)
OUTPUT_ARCH(i386)

SECTIONS {
  .text16 : { *(.text16) }
  .text : { *(.text) }  /* GCC's C code */
  .data : { *(.data) }
  .rodata : { *(.rodata) }
  .bss : {
    __begin_bss = .;
    *(.bss)
    __end_bss = .;
  }
}

And my assembly code could be something like this:
Code: [Select]
; test.asm
;

  bits  16

  ; Symbols defined elsewhere...
  extern __begin_bss
  extern __end_bss
  extern f

  ; Our 16 bits text section.
  section .text16

; Starting here.

  ; Default if up.
  cld

  ; Setup selectors (don't need to setup ss).
  push  cs
  pop   ax
  mov   ds,ax
  mov   es,ax

  ; Clear BSS section.
  lea   edi,[__begin_bss]
  lea   ecx,[__end_bss]
  sub   ecx,edi
  jz    .skip
  xor   eax,eax
  rep   stosb
.skip:

  call  dword f     ; call C f() function.
                    ; dword is necessary here because
                    ; gcc is a 32 bits compiler.

  ; halt
.halt:
  hlt
  jmp   .halt

  section .text

; BIOS tty write routine. Used by C code:
;   __attribute__((fastcall)) void putc( char c );
;
; Entry: cl = char to write.
  global  putc
putc:
  push  bx        ; By SysV calling convention.
  mov   ah,0x0e
  mov   al,cl
  mov   bx,7      ; Attrib white on black, page 0.
  int   0x10
  pop   bx        ; By SysV callign convention.
  retd      ; 32 bits return.

And let's say by function f() is defined as:
Code: [Select]
/* func.c */
extern void putc( char ) __attribute__((fastcall));

void f( void )
{
  static const char msg[] = "Hello!\r\n";
  const char *p;

  p = msg;
  while ( *p )
  {
    putc( *p );
    p++;
  }
}

You must be careful compiling the GCC code because you can't use any standard library functions and your code cannot be a PIE (Position Independent Executable). So, the makefile for this example would be:
Code: [Select]
# Makefile
CFLAGS=-O2 -m16 -ffreestanding -nostdlib -fcf-protection=none -fno-pie

test: test.o func.o
ld -T linker.ld -o $@ $^

test.o: test.asm
nasm -felf32 -o $@ $<

func.o: func.c
Notice the linker will create the "binary" file based on elf32 object file formats.

PS: Even if using -m16 option, GCC's code is still 32 bits code. You must be careful using "far" pointers (not available) and load the appropriate selector (DS) by yourself, maybe using inline assembly.

[]s
Fred
« Last Edit: January 01, 2021, 11:56:10 AM by fredericopissarra »