Search code examples
windowsvisual-studioarm64firmwarebare-metal

What is the process for generating a bare metal binary with MSVC tools?


What is the process for generating a bare metal binary with MSVC tools?

In GNU land, you cc compile or as assemble the sources into object files, ld link the object files into an ELF (with a linker script) and then objcopy the relevant sections out of the ELF as a "firmware binary". I want to do the same thing, but only using MSVC provided tools.

I've been testing with the following ARM64 startup.s:

 AREA .text, CODE, READONLY
start
 LDR w1, =0xDEADBEEF
 B .
 END

This is suppose to simply load the lower 32 bits of the X1 register with 0xDEADBEEF and then spin. To assemble the code I run:

armasm64.exe startup.s

I'm guessing that if I had a peripherals.c source file, I'd need to link startup.s and peripherals.c into a single executable file (COFF?, PE?). Finally, I'd need to strip the any COFF/PE headers so that an ARM MCU can execute the code when loaded.


Solution

  • Disclaimer: I am not within my area of expertise here, and I am proposing an answer based on some tests I did using the MSVC tools, after reading some Microsoft documentation, and three days after the question was asked with no answers having be proposed yet. I hope this answer will trigger more informed answers, so that I may gladly retract it.

    The answer to the question "What is the process for generating a bare metal binary with MSVC tools?" is likely: "There is none".

    aarch64-pe.asm:

     AREA .text, CODE, READONLY
     EXPORT start
    start
     LDR w1, =0xDEADBEEF
     B .
     END
    

    (The symbol 'start' needs to be made public using the EXPORT directive in order to be resolved by the linker).

    Assembling:

    armasm64.exe aarch64-pe.asm
    

    Now, the linker for aarch64 (version 14.28.29334.0) does for example only support a limited list of target subsystems:

    BOOT_APPLICATION,
    CONSOLE,
    WINDOWS,
    NATIVE,
    POSIX,
    EFI_APPLICATION, EFI_BOOT_SERVICE_DRIVER, EFI_ROM, EFI_RUNTIME_DRIVER
    

    From the Microsoft and EFI documentation, it seems all those subsystems require a loader capable of understanding the PECOFF format or to be able to run into the BCD WMI Provider environment in the case of the BOOT_APPLICATION subsystem.

    There is no such thing as a "BAREMETAL" subsystem. When attempting to link aarch64-pe.obj for each of the subsystems but EFI_ROM using 0x0000000040000000 as the base address, the linker exited displaying the same error, complaining that the start address could not be less than 4GiB:

    D:\opt\msvc\arm64>for %I in (BOOT_APPLICATION CONSOLE WINDOWS NATIVE POSIX EFI_APPLICATION EFI_BOOT_SERVICE_DRIVER EFI_ROM EFI_RUNTIME_DRIVER) do link /entry:start /BASE:0x0000000040000000 /subsystem:%I aarch64-pe.obj
    
    D:\opt\msvc\arm64>link /entry:start /BASE:0x0000000040000000 /subsystem:BOOT_APPLICATION aarch64-pe.obj
    Microsoft (R) Incremental Linker Version 14.28.29334.0
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    LINK : fatal error LNK1355: invalid base address 0x40000000; ARM64 image cannot have base address below 4GB
    
    D:\opt\msvc\arm64>link /entry:start /BASE:0x0000000040000000 /subsystem:CONSOLE aarch64-pe.obj
    Microsoft (R) Incremental Linker Version 14.28.29334.0
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    LINK : fatal error LNK1355: invalid base address 0x40000000; ARM64 image cannot have base address below 4GB
    
    D:\opt\msvc\arm64>link /entry:start /BASE:0x0000000040000000 /subsystem:WINDOWS aarch64-pe.obj
    Microsoft (R) Incremental Linker Version 14.28.29334.0
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    LINK : fatal error LNK1355: invalid base address 0x40000000; ARM64 image cannot have base address below 4GB
    
    D:\opt\msvc\arm64>link /entry:start /BASE:0x0000000040000000 /subsystem:NATIVE aarch64-pe.obj
    Microsoft (R) Incremental Linker Version 14.28.29334.0
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    LINK : fatal error LNK1355: invalid base address 0x40000000; ARM64 image cannot have base address below 4GB
    
    D:\opt\msvc\arm64>link /entry:start /BASE:0x0000000040000000 /subsystem:POSIX aarch64-pe.obj
    Microsoft (R) Incremental Linker Version 14.28.29334.0
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    LINK : fatal error LNK1355: invalid base address 0x40000000; ARM64 image cannot have base address below 4GB
    
    D:\opt\msvc\arm64>link /entry:start /BASE:0x0000000040000000 /subsystem:EFI_APPLICATION aarch64-pe.obj
    Microsoft (R) Incremental Linker Version 14.28.29334.0
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    LINK : fatal error LNK1355: invalid base address 0x40000000; ARM64 image cannot have base address below 4GB
    
    D:\opt\msvc\arm64>link /entry:start /BASE:0x0000000040000000 /subsystem:EFI_BOOT_SERVICE_DRIVER aarch64-pe.obj
    Microsoft (R) Incremental Linker Version 14.28.29334.0
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    LINK : fatal error LNK1355: invalid base address 0x40000000; ARM64 image cannot have base address below 4GB
    
    D:\opt\msvc\arm64>link /entry:start /BASE:0x0000000040000000 /subsystem:EFI_ROM aarch64-pe.obj
    Microsoft (R) Incremental Linker Version 14.28.29334.0
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    LINK : warning LNK4075: ignoring '/BASE' due to '/SUBSYSTEM:EFI_ROM' specification
    
    D:\opt\msvc\arm64>link /entry:start /BASE:0x0000000040000000 /subsystem:EFI_RUNTIME_DRIVER aarch64-pe.obj
    Microsoft (R) Incremental Linker Version 14.28.29334.0
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    LINK : fatal error LNK1355: invalid base address 0x40000000; ARM64 image cannot have base address below 4GB
    

    This is likely an issue for a bare-metal embedded system, or may this require an MMU to be available and already configured by a ... bare-metal program starting with the MMU disabled from a start address less than 4GiB.

    When running dumpbin.exe against aarch64-pe.efi, the base address seemed to be set to 0000000180000000 since the ldr instruction was located at 0x0000000180001000, and the file type was set to DLL.

    dumpbin.exe  /disasm aarch64-pe.efi
    File Type: DLL
    
      0000000180001000: 18000041  ldr         w1,0000000180001008
      0000000180001004: 14000000  b           0000000180001004
      0000000180001008: DEADBEEF
    
      Summary
    
            1000 .rdata
            1000 .text
    

    When executing dumpbin.exe against the executables produced by the linker with 0x0000000100000000 as the base address, the file type was consistantly EXECUTABLE IMAGE.

    Even more, dumpbin.exe does not seem to offer converting the resulting executable into a standard format such as s-record or intel hex either.

    My conclusion would therefore be that the MSVC tools alone do not allow building aarch64 bare-metal applications for the time beeing.