如何构建C++ AARCH64-NONE-ELF的图像

发布于 2025-01-31 06:31:20 字数 4958 浏览 2 评论 0原文

一些背景: 我正在为Raspberry Pi 4B编写一个裸机C ++应用程序/OS(以64位模式,因此启动kernel8.fel.s of SD卡),我一直在遇到奇怪的崩溃/挂起(在其中登录到其中在执行非常正常的C ++任务时,ScreenBuffer只是停止而无法解释),例如:

  • 在main()中构造对象(尽管使同一对象都可以
  • 在-O0中构造一个全局的对象),而-O2和-O3倾向于使用-O2和-O3
  • 调用成员函数

我的第一个本能是在我的程序中寻找UB,我可以肯定这不是问题。有问题的CTOR只是调用一些内联ASM来读取PI的型号并相应地设置MMIO基础地址。

我无法公开发布Sourcecode,但我认为(并希望)这是我如何编译/链接我的可执行文件的问题。

我更喜欢根据C ++ 20 stdlib编译/链接,因为我想在此项目中使用std ::数组和选件。

这是我正在使用

ARCH=cortex-a72
TRIPLE=aarch64-none-elf
XDIR:=/PATH/TO/GCC/$(TRIPLE)
GCC_VERSION=11.2.1
XBINDIR:=$(XDIR)/bin
XLIBDIR1:=$(XDIR)/lib
XLIBDIR2:=$(XDIR)/lib/gcc/$(TRIPLE)/$(GCC_VERSION)
XLIBDIR3:=$(XDIR)/$(TRIPLE)/lib
AR:=$(XBINDIR)/$(TRIPLE)-ar
ASM:=$(XBINDIR)/$(TRIPLE)-gcc
CC:=$(XBINDIR)/$(TRIPLE)-gcc
CXX:=$(XBINDIR)/$(TRIPLE)-g++
LD:=$(XBINDIR)/$(TRIPLE)-ld
OBJCOPY:=$(XBINDIR)/$(TRIPLE)-objcopy
RANLIB:=$(XBINDIR)/$(TRIPLE)-ranlib
SIZE:=$(XBINDIR)/$(TRIPLE)-size
STRIP:=$(XBINDIR)/$(TRIPLE)-strip

# COMPILE OPTIONS
WARNINGS=-Wall -Wextra -Wpedantic 
OPTS=-O3
CFLAGS:=-g $(OPTS) -pipe -flto=auto -static-pie -fsigned-char $(WARNINGS) -mcpu=$(ARCH)\
         -static -ffreestanding -nostartfiles
CXXFLAGS:=$(CFLAGS) -std=c++20 -fno-exceptions -fno-unwind-tables -fno-rtti
LDFLAGS:=   -Wl,-nmagic\
            -Wl,-Tlinker.ld\
            -L.\
            -L$(XLIBDIR1)\
            -L$(XLIBDIR2)\
            -L$(XLIBDIR3)\
            -lc\
            -lgcc\
            -lstdc++\
            -Wl,-gc-sections

RM=rm -f
# Source files and include dirs
SOURCES := $(wildcard src/*.cc) $(wildcard src/*.c) $(wildcard src/*.S) $(wildcard test/*cc)

# Create .o and .d files for every .cc and .S (hand-written assembly) file
OBJECTS := $(patsubst %.c, %.o, $(patsubst %.S, %.o, $(patsubst %.cc,%.o,$(SOURCES))))
DEPENDS := $(patsubst %.c, %.d, $(patsubst %.S, %.d, $(patsubst %.cc,%.d,$(SOURCES))))
INC=-Iinclude


# .PHONY means these rules get executed even if
# files of those names exist.
.PHONY: all clean

# The first rule is the default, ie. "make",
# "make all" and "make kernel8.elf" mean the same
all: kernel8.elf

clean:
    $(RM) $(OBJECTS) $(DEPENDS) kernel8.elf kernel8.img

# Linking the executable from the object files
kernel8.elf kernel8.img &: $(OBJECTS) linker.ld
    $(CXX) $(INC) $(CXXFLAGS) $(filter-out %.ld, $^) -o $@ $(LDFLAGS)
    $(OBJCOPY) $@ -O binary kernel8.img

-include $(DEPENDS)

QEMUCMD=qemu-system-aarch64 -M raspi3b\
                            -kernel kernel8.img\
                            -display none\
                            -serial null\
                            -serial stdio\
                            -semihosting\
                            -d unimp

debug: CFLAGS += -DDEBUG -Og
debug: kernel8.img
     $(QEMUCMD) -s -S

run: kernel8.img
    $(QEMUCMD)

%.o: %.cc Makefile
    $(CXX) $(INC) $(CXXFLAGS) -MMD -MP -c $< -o $@ $(LDFLAGS)

%.o: %.c Makefile
    $(CXX) $(INC) $(CFLAGS) -MMD -MP -c $< -o $@ $(LDFLAGS)

此处的linker.ld:


ENTRY(_start)

SECTIONS {
  . = 0x80000;
  .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) }
  .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) }
  PROVIDE(_data = .);
  .data : { *(.data .data.* .gnu.linkonce.d*) }
  .bss (NOLOAD) : {
      . = ALIGN(16);
      __bss_start = .;
      __bss_start__ = __bss_start;
      *(.bss .bss.*)
      *(COMMON)
      __bss_end = .;
  }
  __bss_size = SIZEOF(.bss);
  . = ALIGN(4096);
  .init_array :  {
      __init_array_start = .;
      KEEP (*(.init_array*))
      __init_array_end = .;
  }
  __end = .;

  /* needed for certain newlib routines that (potentially) call _sbrk */
  end = __bss_end;
  __end__ = end;

  __dso_handle = 0;
}

最后,这是我定义的.s文件:

//https://www.rpi4os.com/part1-bootstrapping/
.section ".text.boot"  // Make sure the linker puts this at the start of the kernel image

.global _start  // Execution starts here
.global exit

_start:
    // Check processor ID is zero (executing on main core), else hang
    mrs     x1, mpidr_el1
    and     x1, x1, #3
    // We're not on the main core, so hang in an infinite loop
    cbnz     x1, exit
    // We're on the main core!
    // initialize SP
    ldr     x0, =_start
    mov     sp, x0
    // init global objects and BSS before handing control over to C++
    bl      init
    // Jump to our main() routine in C++ (make sure it doesn't return)
    bl      main
    // if main returns, just spin
exit: b    exit

init surn std :: filt to Zero forto Zero for to Zero .BSS,然后通过CTORS进行迭代在init_array中。

对于我的编译器,我正在使用X86-64 Linux主机上的ARM GNU工具链,我从在这里

我希望我的设置显然有问题,因为我尝试了从C ++的所有常规调试步骤 - 土地无济于事。当调用内联时,完全相同的代码可以正常工作,但是通过方法执行时会悬挂/崩溃。 谢谢!

Some background:
I'm writing a bare-metal C++ app/OS for the Raspberry Pi 4B (in 64-bit mode, so booting kernel8.elf off of an SD card) and I've been running into strange crashes/hangs (where logging to the screenbuffer just stops with no explanation) while doing pretty normal C++ tasks such as:

  • Constructing an object in main() (though making the same object a global seemed to work)
  • Compiling at -O0, while -O2 and -O3 tend to work
  • Calling member functions

My first instinct was to look for UB in my program, and I'm pretty certain that isn't the problem. The ctor in question just calls some inline ASM to read the PI's model number and set the MMIO base address accordingly.

I'm unable to post my sourcecode publicly, but I think (and hope) that this is an issue with how I'm compiling/linking my executable.

I'd prefer to compile/link against the C++20 stdlib, since I want to use std::arrays and optionals throughout this project.

Here's the makefile I'm using

ARCH=cortex-a72
TRIPLE=aarch64-none-elf
XDIR:=/PATH/TO/GCC/$(TRIPLE)
GCC_VERSION=11.2.1
XBINDIR:=$(XDIR)/bin
XLIBDIR1:=$(XDIR)/lib
XLIBDIR2:=$(XDIR)/lib/gcc/$(TRIPLE)/$(GCC_VERSION)
XLIBDIR3:=$(XDIR)/$(TRIPLE)/lib
AR:=$(XBINDIR)/$(TRIPLE)-ar
ASM:=$(XBINDIR)/$(TRIPLE)-gcc
CC:=$(XBINDIR)/$(TRIPLE)-gcc
CXX:=$(XBINDIR)/$(TRIPLE)-g++
LD:=$(XBINDIR)/$(TRIPLE)-ld
OBJCOPY:=$(XBINDIR)/$(TRIPLE)-objcopy
RANLIB:=$(XBINDIR)/$(TRIPLE)-ranlib
SIZE:=$(XBINDIR)/$(TRIPLE)-size
STRIP:=$(XBINDIR)/$(TRIPLE)-strip

# COMPILE OPTIONS
WARNINGS=-Wall -Wextra -Wpedantic 
OPTS=-O3
CFLAGS:=-g $(OPTS) -pipe -flto=auto -static-pie -fsigned-char $(WARNINGS) -mcpu=$(ARCH)\
         -static -ffreestanding -nostartfiles
CXXFLAGS:=$(CFLAGS) -std=c++20 -fno-exceptions -fno-unwind-tables -fno-rtti
LDFLAGS:=   -Wl,-nmagic\
            -Wl,-Tlinker.ld\
            -L.\
            -L$(XLIBDIR1)\
            -L$(XLIBDIR2)\
            -L$(XLIBDIR3)\
            -lc\
            -lgcc\
            -lstdc++\
            -Wl,-gc-sections

RM=rm -f
# Source files and include dirs
SOURCES := $(wildcard src/*.cc) $(wildcard src/*.c) $(wildcard src/*.S) $(wildcard test/*cc)

# Create .o and .d files for every .cc and .S (hand-written assembly) file
OBJECTS := $(patsubst %.c, %.o, $(patsubst %.S, %.o, $(patsubst %.cc,%.o,$(SOURCES))))
DEPENDS := $(patsubst %.c, %.d, $(patsubst %.S, %.d, $(patsubst %.cc,%.d,$(SOURCES))))
INC=-Iinclude


# .PHONY means these rules get executed even if
# files of those names exist.
.PHONY: all clean

# The first rule is the default, ie. "make",
# "make all" and "make kernel8.elf" mean the same
all: kernel8.elf

clean:
    $(RM) $(OBJECTS) $(DEPENDS) kernel8.elf kernel8.img

# Linking the executable from the object files
kernel8.elf kernel8.img &: $(OBJECTS) linker.ld
    $(CXX) $(INC) $(CXXFLAGS) $(filter-out %.ld, $^) -o $@ $(LDFLAGS)
    $(OBJCOPY) $@ -O binary kernel8.img

-include $(DEPENDS)

QEMUCMD=qemu-system-aarch64 -M raspi3b\
                            -kernel kernel8.img\
                            -display none\
                            -serial null\
                            -serial stdio\
                            -semihosting\
                            -d unimp

debug: CFLAGS += -DDEBUG -Og
debug: kernel8.img
     $(QEMUCMD) -s -S

run: kernel8.img
    $(QEMUCMD)

%.o: %.cc Makefile
    $(CXX) $(INC) $(CXXFLAGS) -MMD -MP -c 
lt; -o $@ $(LDFLAGS)

%.o: %.c Makefile
    $(CXX) $(INC) $(CFLAGS) -MMD -MP -c 
lt; -o $@ $(LDFLAGS)

Here's linker.ld:


ENTRY(_start)

SECTIONS {
  . = 0x80000;
  .text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) }
  .rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) }
  PROVIDE(_data = .);
  .data : { *(.data .data.* .gnu.linkonce.d*) }
  .bss (NOLOAD) : {
      . = ALIGN(16);
      __bss_start = .;
      __bss_start__ = __bss_start;
      *(.bss .bss.*)
      *(COMMON)
      __bss_end = .;
  }
  __bss_size = SIZEOF(.bss);
  . = ALIGN(4096);
  .init_array :  {
      __init_array_start = .;
      KEEP (*(.init_array*))
      __init_array_end = .;
  }
  __end = .;

  /* needed for certain newlib routines that (potentially) call _sbrk */
  end = __bss_end;
  __end__ = end;

  __dso_handle = 0;
}

And finally, here's the .S file that I define _start in:

//https://www.rpi4os.com/part1-bootstrapping/
.section ".text.boot"  // Make sure the linker puts this at the start of the kernel image

.global _start  // Execution starts here
.global exit

_start:
    // Check processor ID is zero (executing on main core), else hang
    mrs     x1, mpidr_el1
    and     x1, x1, #3
    // We're not on the main core, so hang in an infinite loop
    cbnz     x1, exit
    // We're on the main core!
    // initialize SP
    ldr     x0, =_start
    mov     sp, x0
    // init global objects and BSS before handing control over to C++
    bl      init
    // Jump to our main() routine in C++ (make sure it doesn't return)
    bl      main
    // if main returns, just spin
exit: b    exit

init just calls std::fill to zero .bss, then iterates through the ctors in init_array.

For my compiler, I'm using the arm GNU toolchain on an x86-64 linux host, which I downloaded from here.

I'm hoping there's something that's just obviously wrong with my setup, since I've tried all the usual debugging steps from C++-land, to no avail. The exact same code works when called inline, but hangs/crashes when executed by a method.
Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

放血 2025-02-07 06:31:20

我找出了我看到的坠机事件的原因。在设置MMU之前,我正在调用MEMSET,而Newlib的优化MEMSET正在生成SIMD商店(指Q0),该商店导致对齐故障和随后的例外。我最终滚动了自己的磁场,然后使用它。

I figured out the cause of the crashes I was seeing. I was calling memset before having set up the MMU, and newlib's optimized memset was generating SIMD stores (referring to q0) which were causing an alignment fault and a subsequent exception. I ended up rolling my own memset and using that instead.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文