Search code examples
ubuntumakefilevirtual-machineubuntu-20.04armv8

Compiling ARMv8 on an Ubuntu VM


I have some c code, a makefile, and code in ARMv8 and am trying to compile it on a virtual machine I set up through Google cloud computing. The VM instance is Ubuntu 20.04 and runs a GNU g++ compiler.

I use this VM to run C++ code for another class and was trying to compile some of my ARMv8 and C code on it with no success. The C/ARMv8 code normally runs on a 64-bit Raspberry Pi 4 and works perfectly on that. Here I was just trying to see if I could just compile the code on the VM so I could just work on the code from my laptop.

Running my makefile in shell returns

gcc -g -O0 -c main.c  -o main.o
gcc -g -O0 -c test.S -o test.o
test.S: Assembler messages:
test.S:6: Error: no such instruction: `adrp X0,msg1'
test.S:7: Error: too many memory references for `add'
test.S:8: Error: no such instruction: `adr X1,msg2'
test.S:9: Error: no such instruction: `adrp X6,num1'
test.S:10: Error: too many memory references for `add'
test.S:11: Error: no such instruction: `ldurb w2,[X6,0]'
test.S:12: Error: no such instruction: `ldursh X3,[X6,1]'
test.S:13: Error: no such instruction: `ldursw X4,[X6,3]'
test.S:14: Error: no such instruction: `ldur X5,[X6,7]'
test.S:15: Error: no such instruction: `br X30'
test.S:21: Warning: value 0xcafebabe truncated to 0xbabe
make: *** [Makefile:24: test.o] Error 1

From my investigation I am guessing I need to get a different compiler and change my makefile but have gotten nowhere. I've attached the code. Any help or input would be appreciated.

Thanks.

C code test.c:

extern long long int test();

int main(void)
{
    test();
    return 0;
}

ARMv8 test.S file:

.section .text
.globl test

test:
    ADRP X0, msg1
    ADD X0,X0, :lo12:msg1
    ADR X1, msg2
    ADRP X6, num1
    ADD X6,X6, :lo12:num1
    LDURB w2, [X6,0]
    LDURSH X3,[X6,1]
    LDURSW X4,[X6,3]
    LDUR X5,[X6,7]
    BR X30
 .section       .data
        msg1:   .asciz "A 17 byte message"
        msg2:   .asciz "Another message of 27 bytes"
        num1:   .byte 45
        num2:   .hword 0xf654
        num3:   .word 0xcafebabe
        num4:   .quad 0xfeedface

Makefile:

TARGET=Lab02
CC=gcc
LD=gcc

FLAGS=-g -O0

all: $(TARGET)

clean:
    rm *.o
    rm $(TARGET)

main.o: main.c
# Compile for best debug view (lowest optimization)
    $(CC) $(FLAGS) -c $^  -o $@

test.o: test.S
# Compile for best debug view (lowest optimization)
    $(CC) $(FLAGS) -c $^ -o $@

$(TARGET): main.o test.o
    $(LD) main.o test.o -o $@


Solution

  • MadScientist is right -- more info would be helpful.

    Assuming your Google VM is an x86_64 (like a pc desktop) instance. Then the default installation of GCC will be targeted at X86_64.

    The thing you want to accomplish is to have an X86_64 vm create an executable that will run on an aarch64 machine (the rpi): the short name for this process is "cross compiling." To compile for aarch64 (rpi 4) on your vm, you need a cross-compiler. Here's the top Google hit for 'linux intro to cross compiling' https://landley.net/writing/docs/cross-compiling.html. That's an OK article.

    from the above article:

    A compiler is a program that turns source code into executable code. Like all programs, a compiler runs on a specific type of computer, and the new programs it outputs also run on a specific type of computer.[1]

    The computer the compiler runs on is called the host, and the computer the new programs run on is called the target. When the host and target are the same type of machine, the compiler is a native compiler. When the host and target are different, the compiler is a cross compiler.

    When talking about the 'type' of a computer, people will often differentiate the different types by referring to their 'machine architecture.' In the current example the two architectures are X86_64 (the vm), and aarch64 (the rpi). The two types of computer have CPUs (and lots of other bits) that operate differently. Not coincidentally, those CPUs are probably manufactured by different companies. The compiler you invoke needs to understand which underlying architecture its output will run on.

    I've not used Ubuntu for a while now, but the general approch for you is going to be:

    1)install a cross compiler: apt install gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu

    2)modify your makefile such that CC and LD point to the cross versions of the compiler/linker:

    CC=aarch64-linux-gnu-gcc
    LD=aarch64-linux-gnu-ld
    

    See this other write up for more details: https://jensd.be/1126/linux/cross-compiling-for-arm-or-aarch64-on-debian-or-ubuntu

    Do note that once you've modified the makefile as I suggest, the resulting makefile will be specific to cross-compiling from X86_64 to aarch64. If you go back to building directly on the rpi, you'll have to use the original makefile, which (on the rpi) will call the native gcc.

    [Edit] One point of interest: your intitial attempt to build your homework project on the VM probably failed because the assembly portion (the .S file) is written in ARM assembly, and the native compiler expects X86_64 assembly.

    [Further Edit] To get a better handle on what is going on, the unix 'file' utility is your friend. Build e.g. a hello-world program with the default/native GCC on your vm. If the compiled executable is e.g. 'hello', run 'file hello' from the command line. you'll get output like

    $ gcc -o hello hello-world.c
    $ file hello
    hello: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=a79795753d9854494b3d0d2bed12efbd08607f0d, for GNU/Linux 4.4.0, stripped
    

    Use the cross-compiler to compile the same hello-world (into, say -- 'hello-arm'). call the file utility on the arm version and you'll get something like:

    $ aarch64-linux-gnueabi-gcc -o hello-arm hello-world.c
    $ file hello-arm
    hello-arm: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0, BuildID[xxHash]=a8d805b895135d1b, with debug_info, not strippedd
    

    The first compiled program ('hello') is built for your vm's native architecture: X86_64. It will run on the vm but, not on the RPi. The second compiled program ('hello-arm') is built for RPi (whose architecture, in this example, is spelled 'aarch64') -- trying to run it on the vm will result in an error, but it will run fine on the Pi.