Help with Assembly. Segmentation fault when compiling samples on Mac OS X - gcc

I'm trying to learn assembly using Dr Paul Carter's pcasm book: http://www.drpaulcarter.com/pcasm/
The author doesn't packaged Mac OS X samples, then I've started using from linux sources. Here is the first sample, that uses his library asm_io.
I'm getting Segmentation Fault when running it. Why? What need to be changed to run in mac?
I think if you know asm, maybe you can tell me what's happening.
Here's the sources.
asm_io.asm:
;
; file: asm_io.asm
; Assembly I/O routines
; To assemble for DJGPP
; nasm -f coff -d COFF_TYPE asm_io.asm
; To assemble for Borland C++ 5.x
; nasm -f obj -d OBJ_TYPE asm_io.asm
; To assemble for Microsoft Visual Studio
; nasm -f win32 -d COFF_TYPE asm_io.asm
; To assemble for Linux
; nasm -f elf -d ELF_TYPE asm_io.asm
; To assemble for Watcom
; nasm -f obj -d OBJ_TYPE -d WATCOM asm_io.asm
; IMPORTANT NOTES FOR WATCOM
; The Watcom compiler's C library does not use the
; standard C calling convention. For example, the
; putchar() function gets its argument from the
; the value of EAX, not the stack.
%define NL 10
%define CF_MASK 00000001h
%define PF_MASK 00000004h
%define AF_MASK 00000010h
%define ZF_MASK 00000040h
%define SF_MASK 00000080h
%define DF_MASK 00000400h
%define OF_MASK 00000800h
;
; Linux C doesn't put underscores on labels
;
%ifdef ELF_TYPE
%define _scanf scanf
%define _printf printf
%define _getchar getchar
%define _putchar putchar
%endif
;
; Watcom puts underscores at end of label
;
%ifdef WATCOM
%define _scanf scanf_
%define _printf printf_
%define _getchar getchar_
%define _putchar putchar_
%endif
%ifdef OBJ_TYPE
segment .data public align=4 class=data use32
%else
segment .data
%endif
int_format db "%i", 0
string_format db "%s", 0
reg_format db "Register Dump # %d", NL
db "EAX = %.8X EBX = %.8X ECX = %.8X EDX = %.8X", NL
db "ESI = %.8X EDI = %.8X EBP = %.8X ESP = %.8X", NL
db "EIP = %.8X FLAGS = %.4X %s %s %s %s %s %s %s", NL
db 0
carry_flag db "CF", 0
zero_flag db "ZF", 0
sign_flag db "SF", 0
parity_flag db "PF", 0
overflow_flag db "OF", 0
dir_flag db "DF", 0
aux_carry_flag db "AF", 0
unset_flag db " ", 0
mem_format1 db "Memory Dump # %d Address = %.8X", NL, 0
mem_format2 db "%.8X ", 0
mem_format3 db "%.2X ", 0
stack_format db "Stack Dump # %d", NL
db "EBP = %.8X ESP = %.8X", NL, 0
stack_line_format db "%+4d %.8X %.8X", NL, 0
math_format1 db "Math Coprocessor Dump # %d Control Word = %.4X"
db " Status Word = %.4X", NL, 0
valid_st_format db "ST%d: %.10g", NL, 0
invalid_st_format db "ST%d: Invalid ST", NL, 0
empty_st_format db "ST%d: Empty", NL, 0
;
; code is put in the _TEXT segment
;
%ifdef OBJ_TYPE
segment text public align=1 class=code use32
%else
segment .text
%endif
global read_int, print_int, print_string, read_char
global print_char, print_nl, sub_dump_regs, sub_dump_mem
global sub_dump_math, sub_dump_stack
extern _scanf, _printf, _getchar, _putchar
read_int:
enter 4,0
pusha
pushf
lea eax, [ebp-4]
push eax
push dword int_format
call _scanf
pop ecx
pop ecx
popf
popa
mov eax, [ebp-4]
leave
ret
print_int:
enter 0,0
pusha
pushf
push eax
push dword int_format
call _printf
pop ecx
pop ecx
popf
popa
leave
ret
print_string:
enter 0,0
pusha
pushf
push eax
push dword string_format
call _printf
pop ecx
pop ecx
popf
popa
leave
ret
read_char:
enter 4,0
pusha
pushf
call _getchar
mov [ebp-4], eax
popf
popa
mov eax, [ebp-4]
leave
ret
print_char:
enter 0,0
pusha
pushf
%ifndef WATCOM
push eax
%endif
call _putchar
%ifndef WATCOM
pop ecx
%endif
popf
popa
leave
ret
print_nl:
enter 0,0
pusha
pushf
%ifdef WATCOM
mov eax, 10 ; WATCOM doesn't use the stack here
%else
push dword 10 ; 10 == ASCII code for \n
%endif
call _putchar
%ifndef WATCOM
pop ecx
%endif
popf
popa
leave
ret
sub_dump_regs:
enter 4,0
pusha
pushf
mov eax, [esp] ; read FLAGS back off stack
mov [ebp-4], eax ; save flags
;
; show which FLAGS are set
;
test eax, CF_MASK
jz cf_off
mov eax, carry_flag
jmp short push_cf
cf_off:
mov eax, unset_flag
push_cf:
push eax
test dword [ebp-4], PF_MASK
jz pf_off
mov eax, parity_flag
jmp short push_pf
pf_off:
mov eax, unset_flag
push_pf:
push eax
test dword [ebp-4], AF_MASK
jz af_off
mov eax, aux_carry_flag
jmp short push_af
af_off:
mov eax, unset_flag
push_af:
push eax
test dword [ebp-4], ZF_MASK
jz zf_off
mov eax, zero_flag
jmp short push_zf
zf_off:
mov eax, unset_flag
push_zf:
push eax
test dword [ebp-4], SF_MASK
jz sf_off
mov eax, sign_flag
jmp short push_sf
sf_off:
mov eax, unset_flag
push_sf:
push eax
test dword [ebp-4], DF_MASK
jz df_off
mov eax, dir_flag
jmp short push_df
df_off:
mov eax, unset_flag
push_df:
push eax
test dword [ebp-4], OF_MASK
jz of_off
mov eax, overflow_flag
jmp short push_of
of_off:
mov eax, unset_flag
push_of:
push eax
push dword [ebp-4] ; FLAGS
mov eax, [ebp+4]
sub eax, 10 ; EIP on stack is 10 bytes ahead of orig
push eax ; EIP
lea eax, [ebp+12]
push eax ; original ESP
push dword [ebp] ; original EBP
push edi
push esi
push edx
push ecx
push ebx
push dword [ebp-8] ; original EAX
push dword [ebp+8] ; # of dump
push dword reg_format
call _printf
add esp, 76
popf
popa
leave
ret 4
sub_dump_stack:
enter 0,0
pusha
pushf
lea eax, [ebp+20]
push eax ; original ESP
push dword [ebp] ; original EBP
push dword [ebp+8] ; # of dump
push dword stack_format
call _printf
add esp, 16
mov ebx, [ebp] ; ebx = original ebp
mov eax, [ebp+16] ; eax = # dwords above ebp
shl eax, 2 ; eax *= 4
add ebx, eax ; ebx = & highest dword in stack to display
mov edx, [ebp+16]
mov ecx, edx
add ecx, [ebp+12]
inc ecx ; ecx = # of dwords to display
stack_line_loop:
push edx
push ecx ; save ecx & edx
push dword [ebx] ; value on stack
push ebx ; address of value on stack
mov eax, edx
sal eax, 2 ; eax = 4*edx
push eax ; offset from ebp
push dword stack_line_format
call _printf
add esp, 16
pop ecx
pop edx
sub ebx, 4
dec edx
loop stack_line_loop
popf
popa
leave
ret 12
sub_dump_mem:
enter 0,0
pusha
pushf
push dword [ebp+12]
push dword [ebp+16]
push dword mem_format1
call _printf
add esp, 12
mov esi, [ebp+12] ; address
and esi, 0FFFFFFF0h ; move to start of paragraph
mov ecx, [ebp+8]
inc ecx
mem_outer_loop:
push ecx
push esi
push dword mem_format2
call _printf
add esp, 8
xor ebx, ebx
mem_hex_loop:
xor eax, eax
mov al, [esi + ebx]
push eax
push dword mem_format3
call _printf
add esp, 8
inc ebx
cmp ebx, 16
jl mem_hex_loop
mov eax, '"'
call print_char
xor ebx, ebx
mem_char_loop:
xor eax, eax
mov al, [esi+ebx]
cmp al, 32
jl non_printable
cmp al, 126
jg non_printable
jmp short mem_char_loop_continue
non_printable:
mov eax, '?'
mem_char_loop_continue:
call print_char
inc ebx
cmp ebx, 16
jl mem_char_loop
mov eax, '"'
call print_char
call print_nl
add esi, 16
pop ecx
loop mem_outer_loop
popf
popa
leave
ret 12
; function sub_dump_math
; prints out state of math coprocessor without modifying the coprocessor
; or regular processor state
; Parameters:
; dump number - dword at [ebp+8]
; Local variables:
; ebp-108 start of fsave buffer
; ebp-116 temp double
; Notes: This procedure uses the Pascal convention.
; fsave buffer structure:
; ebp-108 control word
; ebp-104 status word
; ebp-100 tag word
; ebp-80 ST0
; ebp-70 ST1
; ebp-60 ST2 ...
; ebp-10 ST7
;
sub_dump_math:
enter 116,0
pusha
pushf
fsave [ebp-108] ; save coprocessor state to memory
mov eax, [ebp-104] ; status word
and eax, 0FFFFh
push eax
mov eax, [ebp-108] ; control word
and eax, 0FFFFh
push eax
push dword [ebp+8]
push dword math_format1
call _printf
add esp, 16
;
; rotate tag word so that tags in same order as numbers are
; in the stack
;
mov cx, [ebp-104] ; ax = status word
shr cx, 11
and cx, 7 ; cl = physical state of number on stack top
mov bx, [ebp-100] ; bx = tag word
shl cl,1 ; cl *= 2
ror bx, cl ; move top of stack tag to lowest bits
mov edi, 0 ; edi = stack number of number
lea esi, [ebp-80] ; esi = address of ST0
mov ecx, 8 ; ecx = loop counter
tag_loop:
push ecx
mov ax, 3
and ax, bx ; ax = current tag
or ax, ax ; 00 -> valid number
je valid_st
cmp ax, 1 ; 01 -> zero
je zero_st
cmp ax, 2 ; 10 -> invalid number
je invalid_st
push edi ; 11 -> empty
push dword empty_st_format
call _printf
add esp, 8
jmp short cont_tag_loop
zero_st:
fldz
jmp short print_real
valid_st:
fld tword [esi]
print_real:
fstp qword [ebp-116]
push dword [ebp-112]
push dword [ebp-116]
push edi
push dword valid_st_format
call _printf
add esp, 16
jmp short cont_tag_loop
invalid_st:
push edi
push dword invalid_st_format
call _printf
add esp, 8
cont_tag_loop:
ror bx, 2 ; mov next tag into lowest bits
inc edi
add esi, 10 ; mov to next number on stack
pop ecx
loop tag_loop
frstor [ebp-108] ; restore coprocessor state
popf
popa
leave
ret 4
asm_io.inc:
extern read_int, print_int, print_string
extern read_char, print_char, print_nl
extern sub_dump_regs, sub_dump_mem, sub_dump_math, sub_dump_stack
%macro dump_regs 1
push dword %1
call sub_dump_regs
%endmacro
;
; usage: dump_mem label, start-address, # paragraphs
%macro dump_mem 3
push dword %1
push dword %2
push dword %3
call sub_dump_mem
%endmacro
%macro dump_math 1
push dword %1
call sub_dump_math
%endmacro
%macro dump_stack 3
push dword %3
push dword %2
push dword %1
call sub_dump_stack
%endmacro
first.asm
;
; file: first.asm
; First assembly program. This program asks for two integers as
; input and prints out their sum.
;
; To create executable:
; Using djgpp:
; nasm -f coff first.asm
; gcc -o first first.o driver.c asm_io.o
;
; Using Linux and gcc:
; nasm -f elf first.asm
; gcc -o first first.o driver.c asm_io.o
;
; Using Borland C/C++
; nasm -f obj first.asm
; bcc32 first.obj driver.c asm_io.obj
;
; Using MS C/C++
; nasm -f win32 first.asm
; cl first.obj driver.c asm_io.obj
;
; Using Open Watcom
; nasm -f obj first.asm
; wcl386 first.obj driver.c asm_io.obj
%include "asm_io.inc"
;
; initialized data is put in the .data segment
;
segment .data
;
; These labels refer to strings used for output
;
prompt1 db "Enter a number: ", 0 ; don't forget nul terminator
prompt2 db "Enter another number: ", 0
outmsg1 db "You entered ", 0
outmsg2 db " and ", 0
outmsg3 db ", the sum of these is ", 0
;
; uninitialized data is put in the .bss segment
;
segment .bss
;
; These labels refer to double words used to store the inputs
;
input1 resd 1
input2 resd 1
;
; code is put in the .text segment
;
segment .text
global _asm_main
_asm_main:
enter 0,0 ; setup routine
pusha
mov eax, prompt1 ; print out prompt
call print_string
call read_int ; read integer
mov [input1], eax ; store into input1
mov eax, prompt2 ; print out prompt
call print_string
call read_int ; read integer
mov [input2], eax ; store into input2
mov eax, [input1] ; eax = dword at input1
add eax, [input2] ; eax += dword at input2
mov ebx, eax ; ebx = eax
dump_regs 1 ; dump out register values
dump_mem 2, outmsg1, 1 ; dump out memory
;
; next print out result message as series of steps
;
mov eax, outmsg1
call print_string ; print out first message
mov eax, [input1]
call print_int ; print out input1
mov eax, outmsg2
call print_string ; print out second message
mov eax, [input2]
call print_int ; print out input2
mov eax, outmsg3
call print_string ; print out third message
mov eax, ebx
call print_int ; print out sum (ebx)
call print_nl ; print new-line
popa
mov eax, 0 ; return back to C
leave
ret
drive.c:
#include "cdecl.h"
int PRE_CDECL asm_main( void ) POST_CDECL;
int main()
{
int ret_status;
ret_status = asm_main();
return ret_status;
}
Now I compile it using:
nasm -f macho first.asm
nasm -f macho asm_io.asm
gcc first.o asm_io.o driver.c -o first -arch i386
Then run:
./first
Segmentation fault
It happens only when I'm using asm_io lib.
Thank you,
Daniel Koch

You seem to be using 32-bit assembly code here. One big difference among 32-bit Mac OS X and 32-bit Windows or Linux is that Mac requires the stack to be 16-byte aligned whenever you CALL a function. In other words, at the point in your code where you have a CALL instruction, it is required that ESP = #######0h.
The following may be interesting reads:
http://blogs.embarcadero.com/eboling/2009/05/20/5607
www.agner.org/optimize/calling_conventions.pdf

Related

When I use global variables it crashes my kernel

I have had this happen before and worked around it for a while but now it slowly becomes more and more unavoidable, because now I need them.
For some weird reason, my kernel crashes when I try to use a global variable in my code.
This works:
int global;
void kmain()
{
//do some stuff...
}
This does not work:
int global;
void kmain()
{
global = 1;
//do some stuff...
}
I have no idea why this is happening.
As some additional resources here is my linker script:
OUTPUT_FORMAT(binary)
phys = 0x0500;
SECTIONS
{
.text phys : AT(phys) {
code = .;
*(.text)
*(.rodata)
. = ALIGN(4096);
}
.data : AT(phys + (data - code))
{
data = .;
*(.data)
. = ALIGN(4096);
}
.bss : AT(phys + (bss - code)) {
bss = .;
*(.bss)
. = ALIGN(4096);
}
end = .;
/DISCARD/
: {
*(.comment)
*(.eh_frame)
*(.note.gnu.build-id)
}
}
and my makefile:
bin/UmbrellaOS.img: bin/boot.bin bin/kernel.bin bin/zeros.bin
cat $^ > $#
bin/kernel.bin: tmp/kernel_entry.o tmp/kernel.o
x86_64-elf-ld -o $# -T link.ld $^
tmp/kernel.o: src/kernel/main.c
x86_64-elf-gcc -ffreestanding -m64 -g -c $^ -o $#
Edit:
To be more specific I use QEMU to test my OS upon starting QEMU it instantly closes. It should also be noted that if I try something like this:
int global;
void kmain()
{
return;
global = 0;
}
it works for some reason.
I can see a green L printed to the screen which is the last thing my bootloader does before passing control to the kernel after long mode has been entered.
btw here is my bootloader:
[bits 16]
[org 0x7C00]
KERNEL_LOC equ 0x0500
_start:
mov [_BootDisk], dl
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov bp, 0x7BFF
mov sp, bp
push 0x7E00 ; buffer
push 1 ; sectors to read
push 2 ; sector num
call DiskRead
jc .error
push ebx
pushfd
pop eax
mov ebx, eax
xor eax, 0x200000
push eax
popfd
pushfd
pop eax
cmp eax, ebx
jnz .supported
push _CpuErrorString
call Print
jmp .error
.supported:
mov eax, 0x80000000
cpuid
cmp eax, 0x80000001
jb .no64
mov eax, 0x80000001
cpuid
test edx, 1 << 29
jnz .is64
.no64:
push _64ErrorString
call Print
jmp .error
.is64:
push 0x8000
call MapMem
push KERNEL_LOC ; buffer
push 8 ; sectors to read
push 3 ; sector num
call DiskRead
jc .error
cli
lgdt [GDT_descriptor]
mov eax, cr0
or eax, 1
mov cr0, eax
jmp CODE_SEG:protected_mode
.error:
jmp $
Print:
push bp
mov bp, sp
mov bx, [bp+4]
mov ah, 0x0E
.loop:
mov al, [bx]
cmp al, 0
je .end
int 0x10
inc bx
jmp .loop
.end:
mov sp, bp
pop bp
ret 2
DiskRead:
push bp
mov bp, sp
mov ah, 0x02
mov al, [bp+6]
mov ch, 0
mov cl, [bp+4]
mov dh, 0
mov dl, [_BootDisk]
mov bx, [bp+8]
int 0x13
cmp al, [bp+6]
je .end
jnc .end
push _DiskErrorString
call Print
.end:
mov sp, bp
pop bp
ret 6
MapMem:
push bp
mov bp, sp
mov si, [bp+4]
mov di, [bp+4]
add di, 4
xor ebx, ebx
mov edx, 0x0534D4150
mov eax, 0xE820
mov [di+20], dword 1
mov ecx, 24
int 0x15
jc .failed
mov edx, 0x0534D4150
cmp eax, edx
jne .failed
test ebx, ebx
je .failed
.loop:
mov eax, 0xE820
mov [di+20], dword 1
mov ecx, 24
int 0x15
jc .finish
mov edx, 0x0534D4150
.jmpin:
jcxz .skip
cmp cl, 20
jbe .notext
test byte [di+20], 1
je .skip
.notext:
mov ecx, [di+8]
or ecx, [di+12]
jz .skip
inc dword [si]
add di, 24
.skip:
test ebx, ebx
jne .loop
.finish:
clc
jmp .end
.failed:
push _MemErrorString
call Print
stc
jmp .end
.end:
mov sp, bp
pop bp
ret 2
_BootDisk: db 0
_DiskErrorString: db "Disk read error!", 13, 10, 0
_MemErrorString: db "Memory mapping failed!", 13, 10, 0
_CpuErrorString: db "CPUID not supported!", 13, 10, 0
_64ErrorString: db "x64 bits not supported!", 13, 10, 0
CODE_SEG equ GDT_code - GDT_start
DATA_SEG equ GDT_data - GDT_start
GDT_start:
GDT_null:
dd 0x0
dd 0x0
GDT_code:
dw 0xffff
dw 0x0
db 0x0
db 0b10011010
db 0b11001111
db 0x0
GDT_data:
dw 0xffff
dw 0x0
db 0x0
db 0b10010010
db 0b11001111
db 0x0
GDT_end:
GDT_descriptor:
dw GDT_end - GDT_start - 1
dd GDT_start
times 510-($-$$) db 0
dw 0xAA55
[bits 32]
protected_mode:
mov ax, DATA_SEG
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ebp, 0x90000
mov esp, ebp
call Clear
mov ebx, VGA_MEM
mov byte [ebx], 'P'
inc ebx
mov byte [ebx], 14
mov eax, cr0
and eax, ~(1 << 31)
mov cr0, eax
mov edi, 0x1000
mov cr3, edi
xor eax, eax
mov ecx, 4096
rep stosd
mov edi, cr3
mov dword [edi], 0x2003
add edi, 0x1000
mov dword [edi], 0x3003
add edi, 0x1000
mov dword [edi], 0x4003
add edi, 0x1000
mov ebx, 0x00000003
mov ecx, 512
.set_entry:
mov dword [edi], ebx
add ebx, 0x1000
add edi, 8
loop .set_entry
mov eax, cr4
or eax, 1 << 5
mov cr4, eax
mov ecx, 0xC0000080
rdmsr
or eax, 1 << 8
wrmsr
mov eax, cr0
or eax, 1 << 31
mov cr0, eax
lgdt [GDT.Pointer]
jmp GDT.Code:long_mode
jmp $
Clear:
push ebp
mov ebp, esp
mov ecx, VGA_SIZE
mov eax, VGA_MEM
.loop:
mov byte [eax], 0
inc eax
loop .loop
mov esp, ebp
pop ebp
ret
PRESENT equ 1 << 7
NOT_SYS equ 1 << 4
EXEC equ 1 << 3
RW equ 1 << 1
ACCESSED equ 1 << 0
GRAN_4K equ 1 << 7
SZ_32 equ 1 << 6
LONG_MODE equ 1 << 5
GDT:
.Null: equ $ - GDT
dq 0
.Code: equ $ - GDT
dd 0xFFFF
db 0
db PRESENT | NOT_SYS | EXEC | RW
db GRAN_4K | LONG_MODE | 0xF
db 0
.Data: equ $ - GDT
dd 0xFFFF
db 0
db PRESENT | NOT_SYS | RW
db GRAN_4K | SZ_32 | 0xF
db 0
.TSS: equ $ - GDT
dd 0x00000068
dd 0x00CF8900
.Pointer:
dw $ - GDT - 1
dq GDT
[bits 64]
long_mode:
cli
mov ax, GDT.Data
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov rbp, 0x0007FFFF
mov rsp, rbp
mov rbx, VGA_MEM
mov byte [rbx], 'L'
inc rbx
mov byte [rbx], 2
jmp KERNEL_LOC
VGA_MEM equ 0xB8000
VGA_WIDTH equ 80
VGA_HEIGHT equ 25
VGA_STRIDE equ 2
VGA_SIZE equ VGA_WIDTH * VGA_STRIDE * VGA_HEIGHT
VGA_LENGTH equ VGA_WIDTH * VGA_HEIGHT
times 1024-($-$$) db 0
And for anyone wanting to see the big picture here's the Github repository I made.
The problem was that I simply forgot that I put my page table structures at 0x1000 and accidentally overrode them when loading my kernel at 0x0500.
I ended up leaving the structures at 0x1000 and moved my kernel to 0x5000.
This was a rather simple problem but I would recommend that you still take a look at the comments because there's still a lot of useful information and things to consider.
First: ALWAYS Initialize variables especially global ones.
Second: The problem is surely from your bootloader, can you edit the post to show us how you load your kernel?
Try objdump to see if the variable is declared, use -monitor stdio with QEMU and check the value of CR2 Register, it may be a page fault due to the second problem.
Here is a solution to check if the variable really has a valid pointer:
You can remove these edits after everything is ok.
instead of :
jmp KERNEL_LOC
do:
call KERNEL_LOC ; RAX Has the pointer of the global variable
jmp $
In kmain just type:
return &global
Then run it on QEMU and type in the console info registers, RAX should contain the pointer of the variable named global.

What does DebugView's "Enable Verbose Kernel Output" do?

I've followed Microsoft's "Write a Hello World Windows Driver (KMDF)" and wasn't sure how to see the output on WinDbg. After trying to set the Debug Print Filter registry to 0xFFFFFFFF, rebooting and other rain dance solutions, the one thing that worked was enabling DebugView's "Enable Verbose Kernel Output" option. Now, WinDbg shows debug outputs. Its too verbose but at least it's there.
So what did DebugView modify for WinDbg to show more verbose debug output?
I'm running WinDbg attached to a VM from my Windows host with a bridged connection.
TL;DR: it calls a driver to repeatedly call NtSetDebugFilterState on all kernel components, so that they are all able to print something on the debug output.
Program
Let start with the program itself; there's only one occurrence of the sentence "Enable Verbose Kernel Output":
mov [rsp+78h+mi.wID], 9C7Ch
lea rax, aEnableVerboseK ; "Enable &Verbose Kernel Output"
sbb ecx, ecx
mov [rsp+78h+mi.dwTypeData], rax
and ecx, 8
mov [rsp+78h+mi.fState], ecx
mov rcx, cs:hMenu ; hMenu
call cs:GetSubMenu
mov rcx, rax ; hmenu
lea r9, [rsp+78h+mi] ; lpmi
lea edx, [rdi+3] ; item
lea r8d, [rdi+1] ; fByPosition
call cs:InsertMenuItemA
The above code insert the sub-menu into the main menu. What's important here is the the menu ID, namely 0x9C7C.
This menu ID is used only once more here:
movzx edx, al ; al can either be 0 or 1
xor edi, edi
mov qword ptr [rsp+830h+iNumButtons], rdi ; lpOverlapped
lea rax, [rsp+830h+BytesReturned]
mov [rsp+830h+lpButtons], rax ; lpBytesReturned
xor edx, 1
mov dword ptr [rsp+830h+wBMID], edi ; nOutBufferSize
xor r9d, r9d ; nInBufferSize
xor r8d, r8d ; lpInBuffer
mov [rsp+830h+dwInitParam], rdi ; lpOutBuffer
lea edx, ds:0FFFFFFFF8305003Ch[rdx*4] ; dwIoControlCode
call cs:DeviceIoControl
movzx eax, cs:byte_1400935A3
mov edx, 9C7Ch ; uIDCheckItem
mov rcx, cs:hMenu ; hMenu
mov cs:byte_1400A2776, al
neg al
sbb r8d, r8d
and r8d, 8 ; uCheck
call cs:CheckMenuItem
The above code calls DeviceIoControl and then checks the menu item. The former means the program is actually talking with a device driver.
If we remove a bit of code we can see which IOCTL can be sent to the driver:
movzx edx, al ; al can either be 0 or 1
; snip
xor edx, 1 ; invert AL
; snip
lea edx, ds:0FFFFFFFF8305003Ch[rdx*4] ; dwIoControlCode
call cs:DeviceIoControl
Since RDX can be either 0 or 1 we end up with (base 10):
[rdx*4-2096824260]
Thus:
4 - 2096824260 = -2096824256
0 - 2096824260 = -2096824260
Looking at the handles opened by dbgview64.exe we can see a \Device\dbgv is currently opened.
0: kd> !devobj \Device\dbgv
Device object (ffffd58a97007630) is for:
Dbgv \Driver\DBGV DriverObject ffffd58a8688aaa0
Current Irp 00000000 RefCount 0 Type 00008305 Flags 00000048
SecurityDescriptor ffffe58fb8bdeea0 DevExt 00000000 DevObjExt ffffd58a97007780
ExtensionFlags (0x00000800) DOE_DEFAULT_SD_PRESENT
Characteristics (0000000000)
Device queue is not busy.
0: kd> dt _driver_object ffffd58a8688aaa0
nt!_DRIVER_OBJECT
+0x000 Type : 0n4
+0x002 Size : 0n336
+0x008 DeviceObject : 0xffffd58a`97007630 _DEVICE_OBJECT
+0x010 Flags : 0x12
+0x018 DriverStart : 0xfffff800`dcf90000 Void
+0x020 DriverSize : 0x9000
+0x028 DriverSection : 0xffffd58a`a3ba9be0 Void
+0x030 DriverExtension : 0xffffd58a`8688abf0 _DRIVER_EXTENSION
+0x038 DriverName : _UNICODE_STRING "\Driver\DBGV"
+0x048 HardwareDatabase : 0xfffff800`8372e990 _UNICODE_STRING "\REGISTRY\MACHINE\HARDWARE\DESCRIPTION\SYSTEM"
+0x050 FastIoDispatch : (null)
+0x058 DriverInit : 0xfffff800`dcf97058 long +0
+0x060 DriverStartIo : (null)
+0x068 DriverUnload : (null)
+0x070 MajorFunction : [28] 0xfffff800`dcf91b80 long +0
0: kd> dt nt!_LDR_DATA_TABLE_ENTRY 0xffffd58a`a3ba9be0 Full*
+0x048 FullDllName : _UNICODE_STRING "\??\C:\WINDOWS\system32\Drivers\Dbgv.sys"
So the driver is currently loaded from C:\WINDOWS\system32\Drivers\Dbgv.sys (or you can extract it from the .rsrc section...).
Driver
Looking at the driver, in the driver entry we spot the function used for IRP_MJ_DEVICE_CONTROL:
lea rax, sub_180001B80
mov [rdi+0E0h], rax ; IRP_MJ_DEVICE_CONTROL
mov [rdi+80h], rax
mov [rdi+70h], rax
Inside that function we have the usual setup before calling the right IOCTL:
movzx eax, [rcx+_IO_STACK_LOCATION.MajorFunction]
mov r9d, [rcx+_IO_STACK_LOCATION.Parameters.DeviceIoControl.OutputBufferLength]
mov r10d, [rcx+_IO_STACK_LOCATION.Parameters.DeviceIoControl.IoControlCode]
test al, al ; IRP_MJ_CREATE
jz loc_180001C6C
cmp al, 2 ; IRP_MJ_CLOSE
jz short loc_180001C0C
cmp al, 0Eh ; IRP_MJ_DEVICE_CONTROL
jnz ##CompleteRequest
mov eax, r10d
and eax, 3
cmp al, METHOD_NEITHER
jnz short loc_180001BDF
mov rdx, [rdi+_IRP.UserBuffer]
loc_180001BDF:
mov [rsp+98h+do], r11 ; _DEVICE_OBJECT*
mov [rsp+98h+IoStatus], rbx ; IoStatus
mov [rsp+98h+ioctl], r10d ; IoCtl
mov [rsp+98h+OutputBufferLength], r9d ; OuputBufferLength
mov r9d, [rcx+_IO_STACK_LOCATION.Parameters.DeviceIoControl.InputBufferLength] ; int
mov rcx, [rcx+_IO_STACK_LOCATION.FileObject]
mov qword ptr [rsp+98h+Buffer], rdx ; Buffer
mov dl, 1 ; int
call sub_1800017E0
jmp ##CompleteRequest
Inside the call (sub_1800017E0) we have a big switch for the IOCTL, here's the case -2096824260 (case -2096824256 is slightly different):
loc_1800018B9:
call sub_180002470 ; jumptable 000000018000182F case -2096824260
jmp loc_180001AEB
This function is mostly comprised of two loops:
loc_1800024A0:
xor ebx, ebx
##LoopQuerySetDebugFilter:
mov edx, ebx
mov ecx, esi
call cs:qword_180005438 ; DbgQueryDebugFilterState
mov r8b, 1 ; State
mov edx, ebx ; Level (keeps incrementing up to 0x1E)
mov ecx, esi ; ComponentId (keeps incrementing up to 0x82)
mov [rdi], al ; save current state.
call cs:qword_180005440 ; DbgSetDebugFilterState
inc ebx
inc rdi
cmp ebx, 1Eh
jb short ##LoopQuerySetDebugFilter
inc esi
cmp esi, 82h ; '‚'
jb short loc_1800024A0
Both calls are on DbgQueryDebugFilterState and DbgSetDebugFilterState (reactos source)
which is just a minimal wrapper around NtSetDebugFilterState (reactos source).
As far as we can see the debug filter state is queried, saved, and then set for all kernel components (following is the component tables from the kernel, there are a lot of them):
.rdata:00000001400073E0 KdComponentTable dq offset Kd_SYSTEM_Mask
.rdata:00000001400073E0 ; DATA XREF: NtQueryDebugFilterState+36↓o
.rdata:00000001400073E0 ; NtSetDebugFilterState+43↓o ...
.rdata:00000001400073E8 dq offset Kd_SMSS_Mask
.rdata:00000001400073F0 dq offset Kd_SETUP_Mask
.rdata:00000001400073F8 dq offset Kd_NTFS_Mask
.rdata:0000000140007400 dq offset Kd_FSTUB_Mask
.rdata:0000000140007408 dq offset Kd_CRASHDUMP_Mask
.rdata:0000000140007410 dq offset Kd_CDAUDIO_Mask
.rdata:0000000140007418 dq offset Kd_CDROM_Mask
.rdata:0000000140007420 dq offset Kd_CLASSPNP_Mask
....
Which finally means that all kernel components are able to print something to the debug output.
Note that the other IOCTL just reset the components masks to what they were before checking the menu in the main program.

Reading from STDIN and printing to STDOUT with nasm assembly?

As the title suggest I seem to be having a hard time converting the below code to do the exact same thing, which is to read from stdin and stdout. My professor wants us to stop using int 80h and switch over to using gcc. I've had no problems with reading input with the below code however, switching over to gcc is where I start getting segmentation core dump errors.
section .bss
buf resb 1 ; 1000-byte buffer (in data section)
section .text
global _start
_start:
loop1: mov edx, 1 ; max length
mov ecx, buf ; buffer
mov ebx, 0 ; stdin
mov eax, 3 ; sys_read
int 80h
cmp eax, 0 ; end loop if read <= 0
jle lpend1
mov edx, eax ; length
mov ecx, buf ; buffer
mov ebx, 1 ; stdout
mov eax, 4 ; sys_write
int 80h
jmp loop1 ; go back for more
lpend1:
mov eax, 1
mov ebx, 0
int 80h
My attempt at converting the above to perform the same task
SECTION .data
format: db "%c",0
SECTION .bss
buff: resb 1
SECTION .text
extern printf
extern scanf
global main
main:
loop1:
push buff ;buff will hold the characters in the string/file
push format ;expect character for every buff
call scanf
add esp, 8 ;clear stack
cmp eax, 0 ;if eax is equal to 0 then EOF
je lpend1 ;jump to end main func
xor eax, eax ;clear eax
mov eax, buff ;mov buff to eax register
push eax ;push eax onto the stack
mov eax, format ;mov the string format to eax
push eax ;push onto the stack
call printf ;call printf, prints to screen
add esp, 8 ;clear the stack
jmp loop1 ;jump back to top and repeat
lpend1:
ret ;end of main

Segmentation Fault: 11 in NASM assembled OS/X code that works on Win32

I have this code which compiled perfectly fine (compiled and working) on a windows laptop but when I try it on a Mac, I get a segmentation fault: 11.
This is how I compiled it on a windows:
nasm –f win32 bub.asm
gcc bub.obj –o bub.exe
Compiled at terminal with:
nasm -f macho bub.asm
ld -o bub bub.o -arch i386 -lc -no_pie -macosx_version_min 10.10
./bub
My code:
global _main
extern _printf, _scanf, _system, _getchar
section .text
_main:
pushad
call _clearScreen
call _inputSize
mov ECX, [num_elements]
call _inputNumbers
call _printArray
call _sort
push message
call _printf
add ESP, 4
call _printArray
popad
ret
_sort:
mov EDI, [num_elements]
DEC EDI
do:
mov ESI, 0
mov ECX, EDI
lea EAX, [elements]
lea EBX, [elements + 4]
for:
mov EDX, [EAX]
cmp EDX, [EBX]
JLE endif
xchg edx, [ebx]
mov dword[eax], edx
mov ESI, 1
endif:
add eax, 4
add ebx, 4
LOOP for
cmp ESI, 0
JNE do
ret
_inputSize:
push prompt1
call _printf
add ESP, 4
push num_elements
push scanformat
call _scanf
add ESP, 8
call _getchar
ret
_inputNumbers:
mov EBX, elements
mov EDX, 0
enterNumber:
push EBX
push EDX
push ECX
push prompt2
call _printf
add ESP, 4
push element
push scanformat
call _scanf
add ESP, 8
call _getchar
pop ECX
pop EDX
pop EBX
mov EAX, dword [element]
mov [EBX + EDX], EAX
add EDX, 4
loop enterNumber
ret
_printArray:
push output1
call _printf
add ESP, 4
mov ECX, [num_elements]
mov ESI, 0
printArray:
push ECX
push dword [elements + ESI]
push dword output2
call _printf
add ESP, 8
pop ECX
add ESI, 4
loop printArray
ret
_clearScreen:
push clr
call _system
add ESP, 4
ret
section .data
clr db "cls", 0
scanformat db "%d", 0
prompt1 db "Enter number of elements: ", 0
prompt2 db "Enter number: ", 0
output1 db "Array Contents: ", 0
output2 db "%d ", 0
message db "Array sorted!", 13, 10, 0
bye db "goodbye", 13, 10, 0
num_elements dd 0
elements times 100 dd 0
element dd 0

How to sort signed numbers using assembly language?

I have this homework wherein I need to make a program that asks three SIGNED numbers from the user and my program should be able to sort these numbers in ascending order. I can do it in C++ but I am not that familiar with NASM/Assembly Language.
Here's my code so far:
%include "asm_io.inc"
segment .data
;
; Output strings
;
prompta db "Enter the 1st number: ", 0
promptb db "Enter the 2nd number: ", 0
promptc db "Enter the 3rd number: ", 0
promptd db "The sorted list is: ", 0
segment .bss
input resd 1
segment .text
global _asm_main
_asm_main:
enter 0,0 ; setup routine
pusha
mov eax, prompta
call print_string
call read_int
push eax
mov eax, promptb
call print_string
call read_int
push eax
mov eax, promptc
call print_string
call read_int
push eax
call add_stack
mov ebx, eax
mov eax, promptd
call print_string
mov eax, ebx
call print_int
call print_nl
sub esp, 16
popa
mov eax, 0 ; return back to C
leave
ret
segment .data
; no need for .data
segment .bss
; no need for variables
segment .text
add_stack:
enter 0,0
mov ecx, [ebp+8]
mov ebx, [ebp+12]
mov eax, [ebp+16]
cmp eax, ebx
jg A
cmp ebx, ecx
jg B
cmp ecx, eax
jg C
A:
push eax
B:
push ebx
C:
push ecx
popa
leave
ret
In C++ you cannot change the arguments inside of a function and use it later by the caller, but in assembly you can do everything. You pushed the input onto the stack for later using as arguments for the function add_stack. What about to sort these values and store them back to the original place on the stack:
%include "asm_io.inc"
segment .data
;
; Output strings
;
prompta db "Enter the 1st number: ", 0
promptb db "Enter the 2nd number: ", 0
promptc db "Enter the 3rd number: ", 0
promptd db "The sorted list is: ", 0
segment .text
global _asm_main
_asm_main:
enter 0,0 ; setup routine
pusha
mov eax, prompta
call print_string
call read_int
push eax
mov eax, promptb
call print_string
call read_int
push eax
mov eax, promptc
call print_string
call read_int
push eax
call sort_stack ; Three arguments pushed before
mov eax, promptd
call print_string
mov ecx, 3 ; Pop and write the arguments for `sort_stack`
.print_list:
pop eax
call print_int
mov al, 32
call print_char
loop .print_list
call print_nl
popa
mov eax, 0 ; return back to C
leave
ret
sort_stack:
enter 0,0
mov ecx, [ebp+8]
mov ebx, [ebp+12]
mov eax, [ebp+16]
cmp eax, ebx
jg .1
xchg eax, ebx
.1:
cmp ebx, ecx
jg .2
xchg ebx, ecx
.2:
cmp eax, ebx
jg .3
xchg eax, ebx
.3: ; Write back the registers
mov [ebp+8], ecx
mov [ebp+12], ebx
mov [ebp+16], eax
leave
ret
I'm not sure, if your teacher will like this "trick".

Resources