如何编译裸机hello_world.c并在qemu-system-aarch64上运行它?

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)

它没有给我任何打印。我应该在这里改变什么?

Fra*_*ant 5

您是对的,您可以使用 qemu-system-aarch64 来实现您的目标。\n根据您确切想要做什么,您有多种选择:

\n
    \n
  1. 使用qemu 的半主机模式,以及 gcc--specs=rdimon.specsnewlib,另一个半主机库,例如 Arm 可信固件源代码中提供的库 - 下面的示例使用这种方法。

    \n
  2. \n
  3. 提供您自己的syscalls.c,并使用该--specs=nosys.specs ld选项,以便您可以在裸机程序中使用 newlib:我建议阅读 Balau 博客上 Francesco Balducci 的优秀文章-下面的示例使用这种方法。

    \n
  4. \n
  5. 使用一种更像裸机的方法,如下文所述:它确实使用机器的 UARTsprintf()来显示结果字符串。pl011qemu-virt

    \n
  6. \n
\n

gcc_arm64_ram.ld:

\n
/******************************************************************************\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)\n

qemu-virt-aarch64.ld:

\n
__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)\n

startup.s:

\n
                .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)\n

pl011.c:

\n
#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)\n

pl011.h:

\n
#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)\n

qemu-virt-aarch64.c:

\n
#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

编译:

\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