Cha*_*Kim 3 linux qemu bare-metal linux-kernel arm64
如标题所示,我想编译hello_world.c程序并在qemu-system-aarch64上运行它。这是程序:
#include <stdio.h>
int main()
{
printf("hello world!\n");
}
Run Code Online (Sandbox Code Playgroud)
从https://releases.linaro.org/components/toolchain/binaries/latest-7/aarch64-elf/(这是裸机目录),我可以看到这些工具链:
folder aarch64-elf -
folder aarch64-linux-gnu -
folder aarch64_be-elf -
folder aarch64_be-linux-gnu -
folder arm-eabi -
folder arm-linux-gnueabi -
folder arm-linux-gnueabihf -
folder armeb-eabi -
folder armeb-linux-gnueabi -
folder armeb-linux-gnueabihf -
folder armv8l-linux-gnueabihf
Run Code Online (Sandbox Code Playgroud)
所以我选择了aarch64-elf(这是正确的吗?)并将其安装在我的ubuntu 16.04机器上并将bin目录添加到路径中。如果我这样做,aarch64-elf-gcc hello_world.c
我会收到 _exit、_sbrk、_write、_close、_lseek、_read、_fstat、_isatty 函数的未定义引用错误。所以我尝试添加 -spec=aem.ve-specs 并且它没有抱怨(我不确定这是否正确)。我尝试运行 qemu。
qemu-system-aarch64 -M virt -cpu cortex-a57 -nographic -smp 1 -m 2048 -kernel a.out
Run Code Online (Sandbox Code Playgroud)
它没有给我任何打印。我应该在这里改变什么?
您是对的,您可以使用 qemu-system-aarch64 来实现您的目标。\n根据您确切想要做什么,您有多种选择:
\n使用qemu 的半主机模式,以及 gcc--specs=rdimon.specs
和newlib,
另一个半主机库,例如 Arm 可信固件源代码中提供的库 - 下面的示例使用这种方法。
提供您自己的syscalls.c
,并使用该--specs=nosys.specs
ld
选项,以便您可以在裸机程序中使用 newlib:我建议阅读 Balau 博客上 Francesco Balducci 的优秀文章-下面的示例使用这种方法。
使用一种更像裸机的方法,如下文所述:它确实使用机器的 UARTsprintf()
来显示结果字符串。pl011
qemu-virt
gcc_arm64_ram.ld
:
/******************************************************************************\n * @file gcc_arm32.ld\n * @brief GNU Linker Script for Cortex-M based device\n * @version V2.0.0\n * @date 21. May 2019\n ******************************************************************************/\n/*\n * Copyright (c) 2009-2019 Arm Limited. All rights reserved.\n *\n * SPDX-License-Identifier: Apache-2.0\n *\n * Licensed under the Apache License, Version 2.0 (the License); you may\n * not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an AS IS BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nMEMORY\n{\n RAM (rwx) : ORIGIN = __RAM_BASE, LENGTH = __RAM_SIZE\n}\n\n/* Linker script to place sections and symbol values. Should be used together\n * with other linker script that defines memory regions FLASH and RAM.\n * It references following symbols, which must be defined in code:\n * Reset_Handler : Entry of reset handler\n *\n * It defines following symbols, which code can use without definition:\n * __exidx_start\n * __exidx_end\n * __copy_table_start__\n * __copy_table_end__\n * __zero_table_start__\n * __zero_table_end__\n * __etext\n * __data_start__\n * __preinit_array_start\n * __preinit_array_end\n * __init_array_start\n * __init_array_end\n * __fini_array_start\n * __fini_array_end\n * __data_end__\n * __bss_start__\n * __bss_end__\n * __end__\n * end\n * __HeapLimit\n * __StackLimit\n * __StackTop\n * __stack\n */\nENTRY(Reset_Handler)\n\nSECTIONS\n{\n .text :\n {\n KEEP(*(.vectors))\n *(.text*)\n\n KEEP(*(.init))\n KEEP(*(.fini))\n\n /* .ctors */\n *crtbegin.o(.ctors)\n *crtbegin?.o(.ctors)\n *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)\n *(SORT(.ctors.*))\n *(.ctors)\n\n /* .dtors */\n *crtbegin.o(.dtors)\n *crtbegin?.o(.dtors)\n *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)\n *(SORT(.dtors.*))\n *(.dtors)\n\n *(.rodata*)\n\n KEEP(*(.eh_frame*))\n } > RAM\n\n /*\n * SG veneers:\n * All SG veneers are placed in the special output section .gnu.sgstubs. Its start address\n * must be set, either with the command line option \xef\xbf\xbd--section-start\xef\xbf\xbd or in a linker script,\n * to indicate where to place these veneers in memory.\n */\n/*\n .gnu.sgstubs :\n {\n . = ALIGN(32);\n } > RAM\n*/\n .ARM.extab :\n {\n *(.ARM.extab* .gnu.linkonce.armextab.*)\n } > RAM\n\n __exidx_start = .;\n .ARM.exidx :\n {\n *(.ARM.exidx* .gnu.linkonce.armexidx.*)\n } > RAM\n __exidx_end = .;\n\n .copy.table :\n {\n . = ALIGN(16);\n __copy_table_start__ = .;\n LONG (__etext)\n LONG (__data_start__)\n LONG (__data_end__ - __data_start__)\n /* Add each additional data section here */\n/*\n LONG (__etext2)\n LONG (__data2_start__)\n LONG (__data2_end__ - __data2_start__)\n*/\n __copy_table_end__ = .;\n } > RAM\n\n .zero.table :\n {\n . = ALIGN(16);\n __zero_table_start__ = .;\n /* Add each additional bss section here */\n/*\n LONG (__bss2_start__)\n LONG (__bss2_end__ - __bss2_start__)\n*/\n __zero_table_end__ = .;\n } > RAM\n\n /**\n * Location counter can end up 2byte aligned with narrow Thumb code but\n * __etext is assumed by startup code to be the LMA of a section in RAM\n * which must be 4byte aligned \n */\n __etext = ALIGN(16);\n\n .data : AT (__etext)\n {\n __data_start__ = .;\n *(vtable)\n *(.data)\n *(.data.*)\n\n . = ALIGN(16);\n /* preinit data */\n PROVIDE_HIDDEN (__preinit_array_start = .);\n KEEP(*(.preinit_array))\n PROVIDE_HIDDEN (__preinit_array_end = .);\n\n . = ALIGN(16);\n /* init data */\n PROVIDE_HIDDEN (__init_array_start = .);\n KEEP(*(SORT(.init_array.*)))\n KEEP(*(.init_array))\n PROVIDE_HIDDEN (__init_array_end = .);\n\n\n . = ALIGN(16);\n /* finit data */\n PROVIDE_HIDDEN (__fini_array_start = .);\n KEEP(*(SORT(.fini_array.*)))\n KEEP(*(.fini_array))\n PROVIDE_HIDDEN (__fini_array_end = .);\n\n KEEP(*(.jcr*))\n . = ALIGN(16);\n /* All data end */\n __data_end__ = .;\n\n } > RAM\n\n /*\n * Secondary data section, optional\n *\n * Remember to add each additional data section\n * to the .copy.table above to asure proper\n * initialization during startup.\n */\n/*\n __etext2 = ALIGN(16);\n\n .data2 : AT (__etext2)\n {\n . = ALIGN(16);\n __data2_start__ = .;\n *(.data2)\n *(.data2.*)\n . = ALIGN(16);\n __data2_end__ = .;\n\n } > RAM2\n*/\n\n .bss :\n {\n . = ALIGN(16);\n __bss_start__ = .;\n *(.bss)\n *(.bss.*)\n *(COMMON)\n . = ALIGN(16);\n __bss_end__ = .;\n } > RAM AT > RAM\n\n /*\n * Secondary bss section, optional\n *\n * Remember to add each additional bss section\n * to the .zero.table above to asure proper\n * initialization during startup.\n */\n/*\n .bss2 :\n {\n . = ALIGN(16);\n __bss2_start__ = .;\n *(.bss2)\n *(.bss2.*)\n . = ALIGN(16);\n __bss2_end__ = .;\n } > RAM2 AT > RAM2\n*/\n\n .heap (COPY) :\n {\n . = ALIGN(16);\n __end__ = .;\n PROVIDE(end = .);\n . = . + __HEAP_SIZE;\n . = ALIGN(16);\n __HeapLimit = .;\n } > RAM\n\n .stack (ORIGIN(RAM) + LENGTH(RAM) - __STACK_SIZE) (COPY) :\n {\n . = ALIGN(16);\n __StackLimit = .;\n . = . + __STACK_SIZE;\n . = ALIGN(16);\n __StackTop = .;\n } > RAM\n PROVIDE(__stack = __StackTop);\n\n /* Check if data + heap + stack exceeds RAM limit */\n ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack")\n}\n
Run Code Online (Sandbox Code Playgroud)\nqemu-virt-aarch64.ld
:
__RAM_BASE = 0x40000000;\n__RAM_SIZE = 0x08000000;\n__STACK_SIZE = 0x00100000;\n__HEAP_SIZE = 0x00100000;\nINCLUDE gcc_arm64_ram.ld\n
Run Code Online (Sandbox Code Playgroud)\nstartup.s
:
.title startup64.s\n .arch armv8-a\n .text\n .section .text.startup,"ax" \n .globl Reset_Handler\nReset_Handler:\n ldr x0, =__StackTop\n mov sp, x0\n bl main\nwait: wfe\n b wait\n .end\n
Run Code Online (Sandbox Code Playgroud)\npl011.c
:
#include <stdint.h>\n\nstatic volatile unsigned int * const UART0DR = ( unsigned int * ) ( uintptr_t * ) 0x9000000;\n\nint putchar(int c)\n{\n *UART0DR = c; /* Transmit char */\n return c;\n}\n\nvoid putchar_uart0( int c )\n{\n *UART0DR = c; /* Transmit char */\n}\n\nvoid putc_uart0( int c )\n{\n *UART0DR = c; /* Transmit char */\n}\n\nvoid print_uart0( const char * s )\n{\n while( *s != \'\\0\' ) /* Loop until end of string */\n {\n *UART0DR = ( unsigned int ) ( *s ); /* Transmit char */\n s++; /* Next char */\n }\n}\n\nvoid puts_uart0( const char * s )\n{\n while( *s != \'\\0\' ) /* Loop until end of string */\n {\n *UART0DR = ( unsigned int ) ( *s ); /* Transmit char */\n if (*s == \'\\n\') {\n *UART0DR = ( unsigned int ) ( \'\\r\' );\n } \n s++; /* Next char */\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\npl011.h
:
#pragma once\n\n#ifdef __cplusplus\nextern "C" {\n#endif\n\nvoid putchar_uart0( int c );\nvoid print_uart0( const char * s );\nvoid putc_uart0( int c );\nvoid puts_uart0( const char * s );\n\n#ifdef __cplusplus\n}\n#endif\n
Run Code Online (Sandbox Code Playgroud)\nqemu-virt-aarch64.c
:
#include <stdio.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <unistd.h>\n#include <errno.h>\n\n#include "pl011.h"\n\n// angel/semihosting interface\n#define SYS_WRITE0 0x04 \nstatic uint64_t semihosting_call(uint32_t operation, uint64_t parameter)\n{\n __asm("HLT #0xF000");\n}\n\n// syscall stubs\nint _close (int fd)\n{\n errno = EBADF;\n return -1;\n}\n\nint _isatty (int fd)\n{\n return 1;\n}\n\nint _fstat (int fd, struct stat * st)\n{\n errno = EBADF;\n return -1;\n}\n\noff_t _lseek (int fd, off_t ptr, int dir)\n{\n errno = EBADF;\n return (off_t) -1;\n}\n\nint _read (int fd, void *ptr, size_t len)\n{\n errno = EBADF;\n return -1;\n}\n\nint _write (int fd, const char *ptr, size_t len)\n{\n for (size_t i = 0; i < len; i++) {\n putchar_uart0(ptr[i]);\n }\n return len;\n}\n\nvoid main()\n{\n char buffer[BUFSIZ];\n uint64_t regCurrentEL;\n\n __asm volatile ("mrs %0, CurrentEL" : "=r" (regCurrentEL));\n\n // UART0\n sprintf(buffer, "Hello EL%d World!\\n", (regCurrentEL >> 2) & 0b11);\n puts_uart0(buffer);\n\n // angel/semihosting interface\n sprintf(buffer, "Hello semi-hosted EL%d World!\\n", (regCurrentEL >> 2) & 0b11);\n semihosting_call(SYS_WRITE0, (uint64_t) (uintptr_t) buffer);\n\n // newlib - custom syscalls.c, with _write() using UART0\n printf("Hello EL%d World! (syscalls version)\\n", (regCurrentEL >> 2) & 0b11);\n}\n
Run Code Online (Sandbox Code Playgroud)\n请注意,负责初始化该.bss
部分的代码被省略。
编译:
\n/opt/arm/9/gcc-arm-9.2-2019.12-x86_64-aarch64-none-elf/bin/aarch64-none-elf-gcc -I. -O0 -ggdb -mtune=cortex-a53 -nostartfiles -ffreestanding --specs=nosys.specs -L. -Wl,-T,qemu-virt-aarch64.ld -o virt.elf startup.s pl011.c qemu-virt-aarch64.c \n
Run Code Online (Sandbox Code Playgroud)\n跑步:
\n/opt/qemu-5.2.0/bin/qemu-system-aarch64 -semihosting -m 128M -nographic -monitor none -serial stdio -machine virt,gic-version=2,secure=on,virtualization=on -cpu cortex-a53 -kernel virt.elf\nHello EL3 World!\nHello semi-hosted EL3 World!\nHello EL3 World! (syscalls version)\n
Run Code Online (Sandbox Code Playgroud)\n