Linker anomaly & GRUB

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
p34c3
Posts: 3
Joined: Tue Mar 03, 2020 11:48 am

Linker anomaly & GRUB

Post by p34c3 »

Hey guys :)
I sadly lost the credentials to my old account but recently planned to do a small upgrade to my build environment for my OS.
The idea was to use clang so I dont need any crosscompilers which worked very nicely on windows.
Now I wanted to compile the exact same code on WSL/Linux and the compilation worked, sadly the OS didnt boot.
It took me a while to figure out whats wrong (especially because the code is pretty small), anyways it seems like there is a problem with the stack.

Code: Select all

.code32
.section .text, "ax"

.extern init

.global _start
_start:
	cli
	mov esp, tmp_stack+0x2000
	push 0x2f4b2f4f
	pop eax
	mov dword ptr [0xb8000], eax

_stop:
	cli
	hlt
	jmp _stop

.section .bss, "aw"
.comm tmp_stack, 0x2000, 0x1000
Linker script:

Code: Select all

ENTRY(_start)

SECTIONS
{
	. = 0x400000;
	
	.text :
	{
		*(multiboot)
		*(.text)
	}
	
	.data ALIGN(4096) :
	{
		*(.data)
	}
	
	.rodata ALIGN(4096) :
	{
		*(.rodata)
	}
	
	.bss ALIGN(4096) :
	{
		*(.bss)
	}
	
	/DISCARD/ :
	{
		*(.comment)
		*(.eh_frame)
		*(.note.gnu.build-id)
	}
}
Compile flags:

Code: Select all

SET(COMMON_FLAGS "-Wall -Wextra -pedantic -Werror ${COMMON_FLAGS}" CACHE STRING "" FORCE)
SET(COMMON_FLAGS "-target i386-none-elf -mno-mmx -mno-sse -mno-sse2 ${COMMON_FLAGS}" CACHE STRING "" FORCE)

SET(CMAKE_ASM_FLAGS "${COMMON_FLAGS} -masm=intel ${CMAKE_ASM_FLAGS}" CACHE STRING "" FORCE)
SET(CMAKE_C_FLAGS "${COMMON_FLAGS} -ffreestanding ${CMAKE_C_FLAGS}" CACHE STRING "" FORCE)
SET(CMAKE_CXX_FLAGS "${COMMON_FLAGS} -ffreestanding -fno-exceptions -fno-rtti ${CMAKE_CXX_FLAGS}" CACHE STRING "" FORCE)

SET(CMAKE_EXE_LINKER_FLAGS_INIT "-target i386-linux-elf -nostdlib")
IF(WIN32)
	SET(CMAKE_EXE_LINKER_FLAGS_INIT "${CMAKE_EXE_LINKER_FLAGS_INIT} -fuse-ld=lld.exe")
ELSEIF(APPLE)
	SET(CMAKE_EXE_LINKER_FLAGS_INIT "${CMAKE_EXE_LINKER_FLAGS_INIT} -fuse-ld=lld")
ENDIF()
It does not print a green OK but only zeros (It simply makes the text at the first 2 chars go blank).
A normal mov without using the stack works fine.

Im really not sure why this happens :(
Thanks in advance!
Last edited by p34c3 on Wed Mar 04, 2020 5:14 pm, edited 1 time in total.
nullplan
Member
Member
Posts: 1792
Joined: Wed Aug 30, 2017 8:24 am

Re: Linker anomaly & GRUB

Post by nullplan »

My advice: Run it through a disassembler. Is the value put into ESP reasonable? If not, then you might be setting your stack to point to MMIO, and maybe this kind of MMIO swallows your writes and returns zero on read.

I am unfamiliar with GAS's Intel mode, therefore I cannot give advice there, but the line initializing ESP looks to me like it might access the memory.
Carpe diem!
Octocontrabass
Member
Member
Posts: 5575
Joined: Mon Mar 25, 2013 7:01 pm

Re: Linker anomaly & GRUB

Post by Octocontrabass »

nullplan wrote:the line initializing ESP looks to me like it might access the memory.
It does. Intel syntax is not the same thing as NASM syntax. In Intel syntax, labels are always memory references.

Personally, I suggest switching to an assembler that uses NASM syntax, such as NASM. If you don't want to do that right now, try replacing that MOV with LEA and see if it works any better.
p34c3
Posts: 3
Joined: Tue Mar 03, 2020 11:48 am

Re: Linker anomaly & GRUB

Post by p34c3 »

Oh my god, how could I miss that xD
Im staring at IDA the whole day but didnt see that it dereferences the value... thanks a ton guys :P

I sadly hit another weird problem right afterwards.. I now have this C code which is called in entrypoint:
Entrypoint:

Code: Select all

_start:
	lea esp, tmp_stack + 0x2000
	call init
	push 0x2f4b2f4f
	pop eax
	mov dword ptr [0xb8000], eax
C Code:

Code: Select all

void init()
{
	char *video = (char*)0xB8000;

	//volatile char msg[] = "Hello World!";
	const char *msg = "Hello World!";
	
	for(int i = 0; i < 12; i++)
	{
		video[2*i] = msg[i];
		video[2*i + 1] = 0x07;
	}
}
As soon as I try to use the image's .rodata section it very often returns zeroed data which doesnt really make any sense.
If I switch to the volatile char array on stack it works just fine, so the problem must be that something/someone is overwriting that .rodata
I will try to boot with BOCHS so I have a chance of debugging this.


EDIT:
I actually dumped the memory and noticed that its infact gone, but the windows build works again.
I compared the ELF headers and noticed this:

Code: Select all

$ readelf -l kernel_win.bin

Elf file type is EXEC (Executable file)
Entry point 0x40000c
There are 3 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x001000 0x00400000 0x00400000 0x0100d 0x0100d R E 0x1000
  LOAD           0x003000 0x00402000 0x00402000 0x00000 0x02000 RW  0x1000
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0

 Section to Segment mapping:
  Segment Sections...
   00     .text .rodata
   01     .bss
   02



$ readelf -l kernel_lin.bin

Elf file type is EXEC (Executable file)
Entry point 0x40000c
There are 4 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x001000 0x00400000 0x00400000 0x0007e 0x0007e R E 0x1000
  LOAD           0x002000 0x00401000 0x00403e80 0x0000d 0x0000d R   0x1000
  LOAD           0x003000 0x00402000 0x00404e80 0x00000 0x02000 RW  0x1000
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x10

 Section to Segment mapping:
  Segment Sections...
   00     .text
   01     .rodata
   02     .bss
   03
Might this have something to do with physical addresses being different than the virtual addresses?
Im not sure if grub can handle that properly (but it should no?)
Infact the Hello World can be found at memory address 0x00403e80 (PHYS) but not at address 0x00401000 (VIRT) ... why is GRUB only loading my binary flat?




EDIT2:
I Solved this nonstandard behaviour by adding these "AT" commands in my linker script, it would be cool to know why the linker has this nonstandard behaviour of choosing VMA != LMA without any reason.

Code: Select all

ENTRY(_start)

SECTIONS
{
	. = 0x400000;
	
	.text :
	{
		*(multiboot)
		*(.text)
	}
	
	.data ALIGN(4096) : AT ( ADDR (.data))
	{
		*(.data)
	}
	
	.rodata ALIGN(4096) : AT ( ADDR (.rodata) )
	{
		*(.rodata*)
	}
	
	.bss ALIGN(4096) : AT ( ADDR (.bss) )
	{
		*(.bss)
	}
	
	/DISCARD/ :
	{
		*(.comment)
		*(.eh_frame)
		*(.note.gnu.build-id)
	}
}


Thanks a ton again for the help! :)
Post Reply