0

I am trying to write Assembly on MacOS using the default clang assembler.

I got this small "Hello World" working for MacOS on the same machine:

.global _main
.align 2

_main: mov X0, #1
    adr     X1, hello
    mov     X2, #13
    mov     X16, #4
    svc     0

    mov     X0, #0
    mov     X16, #1
    svc     0

hello: .ascii  "Hello World!\n"

It is taken from this question.

When I assembler it with:

clang hello.s

I can successfully run it with:

./a.out

Now, I try to follow the "Hello World" for Linux from the book Programming with 64-Bit ARM Assembly Language. It is basically the same program, but the sys-calls is for Linux:

.global _start

_start: mov X0, #1
    ldr     X1, =hello
    mov     X2, #13
    mov     X8, #64
    svc     0

    mov     X0, #0
    mov     X8, #93
    svc     0

.data
hello: .ascii  "Hello World!\n"

I cannot event assemble this program using clang hello.s on MacOS, I get this error:

Undefined symbols for architecture arm64:
  "_main", referenced from:
     implicit entry/start for main executable
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

I assume that this error is because I have not told clang that it should use Linux as target.

How can I cross assemble the above "Hello World" to Linux Arm64 from MacOS on Arm64 using the built in clang?

I want to be able to run the Linux binary in a Arm64 Docker container on MacOS Arm64.

Jonas
  • 121,568
  • 97
  • 310
  • 388
  • 1
    The code is missing a `=` sign. Should be `ldr X1, =hello` to load the address. Try if that works better. – Jester Dec 08 '22 at 19:34
  • You used `ldr` instead of `adr` in the Linux version. And not even `ldr X1, =hello` to tell the assembler to get the address somehow, you asked it to load 8 bytes from memory. Apparently it doesn't want to expand that as a pseudo-instruction, so it's trying to use a PC-relative addressing mode or something, but it has to reach a potentially long distance into another section? Not sure what relocation type it thinks it wants. – Peter Cordes Dec 08 '22 at 19:35
  • @PeterCordes It wants something like `adrp`+`add` with a `.loh AdrpAdd ...` directive, see [here](https://stackoverflow.com/a/74677682). But that's just for Darwin targets anyway. – Siguza Dec 08 '22 at 19:38
  • @Jester thanks, that was my typo. I updated the error message after fixing the typo. – Jonas Dec 08 '22 at 19:40
  • Try with `-nostdlib`. – Jester Dec 08 '22 at 19:42

2 Answers2

1

You will need lld or some other linker that can output ELF binaries, as Xcode only ships with Apple's ld64, which is limited to Darwin targets. Once you have lld in your path, you can build your assembly file with:

clang --target=arm64-linux-gnu -fuse-ld=lld hello.s -nostdlib

Of course, if you want to use libraries or eventually compile C code and include header files, you will have to either find or build an SDK to pass to clang with --sysroot. I'm not aware of any standard process for doing this, but I've had some success with just pulling public deb files from Ubuntu/Debian package mirrors and unpacking them into one folder.

Siguza
  • 21,155
  • 6
  • 52
  • 89
  • Apple `clang` does not seem to be compatible with `lld`. I get errors like `error: unknown argument '--eh-frame-hdr'` and same for `argument '-EL'`. – Jonas Dec 09 '22 at 17:42
  • @Jonas `--eh-frame-hdr` and `-EL` are `lld`-specific arguments though. I think you just installed a too-old version of `lld`. – Siguza Dec 12 '22 at 16:46
1

After the answer to Siguza, the difficult part was to find a linker that could do the job.

This is what finally worked for me.

Install macos cross toolchain with:

brew install aarch64-unknown-linux-gnu

After that I have this binary that can be added to PATH:

/opt/homebrew/Cellar/aarch64-unknown-linux-gnu/11.2.0/bin/aarch64-unknown-linux-gnu-ld

Then I could build the binary with:

clang --target=arm64-linux-gnu -fuse-ld=aarch64-unknown-linux-gnu-ld hello.s -nostdlib

And I could verify the binary by using this Dockerfile:

FROM scratch
ADD a.out /bin/
ENTRYPOINT ["/bin/a.out"]

Build a container with:

docker build -t app .

Run it and get the output:

docker run app
Hello World!
Jonas
  • 121,568
  • 97
  • 310
  • 388