Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android Clang cross-compilation #39

Closed
anonymix007 opened this issue Mar 24, 2024 · 26 comments
Closed

Android Clang cross-compilation #39

anonymix007 opened this issue Mar 24, 2024 · 26 comments

Comments

@anonymix007
Copy link

I've been trying to build gnat-llvm with clang-r475365b and after some changes it builds just fine. However, compiling Ada programs doesn't work. Shared library build fails at the linking stage:

ld.lld: error: relocation R_AARCH64_ADR_PREL_PG_HI21 cannot be used against symbol 'system__soft_links__abort_defer'; recompile with -fPIC
ld.lld: error: relocation R_AARCH64_LDST64_ABS_LO12_NC cannot be used against symbol 'system__soft_links__abort_defer'; recompile with -fPIC
ld.lld: error: relocation R_AARCH64_ADR_PREL_PG_HI21 cannot be used against symbol 'system__soft_links__abort_undefer'; recompile with -fPIC
ld.lld: error: relocation R_AARCH64_LDST64_ABS_LO12_NC cannot be used against symbol 'system__soft_links__abort_undefer'; recompile with -fPIC
ld.lld: error: relocation R_AARCH64_ADR_PREL_PG_HI21 cannot be used against symbol 'program_error'; recompile with -fPIC
ld.lld: error: relocation R_AARCH64_ADD_ABS_LO12_NC cannot be used against symbol 'program_error'; recompile with -fPIC

I believe this is due to libgnat_llvm.a built for x86-64. Is there a way to build it for several architectures at once?

@ArnaudCharlet
Copy link
Member

You cannot build libgnat for several architectures at once, although here you may be missing a -fPIC switch instead when building the library? Since we don't know which change you made to the build and what platform/environment you are targetting, it's hard to comment further.

@anonymix007
Copy link
Author

-fPIC flag is present. So unless it's not working properly, it shouldn't be the issue.

I'm targeting Android (Bionic libc, at least aarch64, ideally arm as well).
Do you have any suggestions on how to build it? I guess I can just build it twice for different architectures, but I don't see an easy way to build for anything except host.

Changes were made to fix build with Android clang (just a few backports, also had to replace std::optional with llvm optional), I can upload them to GitHub later today.

@anonymix007
Copy link
Author

Object files are built using the following command (maybe with a different order, I'll make a minimal reproducible example later):

./llvm-gcc --target=aarch64-linux-android29 -fPIC -c -gnat2022 -O3 file.adb

The resulting file.o appears to have correct architecture (aarch64), but has those incompatible relocations.

In fact, I even tried to build with --emit-llvm and then used llc -relocation-model=pic with the same exact issue. Are the relocation type stored in LLVM IR?

@ArnaudCharlet
Copy link
Member

ArnaudCharlet commented Mar 25, 2024 via email

@ArnaudCharlet
Copy link
Member

ArnaudCharlet commented Mar 25, 2024 via email

@anonymix007
Copy link
Author

make -C llvm-interface CFLAGS="-O2 --target=..."

Will it build cross-compiler or the resulting llvm-gcc will be for aarch64?

@Lucretia
Copy link

Lucretia commented Mar 25, 2024

Can you upload your fork to github? I tried to compile gcc/gnat for Android last week and it failed because of headers, so having a build available would be useful for me.

In your fork, can you specify the installed NDK's llvm?

@anonymix007
Copy link
Author

I'll upload all my changes later today.
I actually haven't used NDK because it lacks necessary LLVM libraries and executables (i.e.opt), one will need to build it from sources. Luckily, google has a build script (which I had to patch to retain those LLVM libraries and executables)

@Lucretia
Copy link

TBH, I'm not really bothered how it works as long as it does.

@anonymix007
Copy link
Author

It currently doesn't. That's why this issue exists.

I found somewhat similar issue and -Wl,-Bsymbolic helped. I'll try it when I get back to my PC.

@ArnaudCharlet
Copy link
Member

make -C llvm-interface CFLAGS="-O2 --target=..."

Will it build cross-compiler or the resulting llvm-gcc will be for aarch64?

No, CFLAGS only influences the runtime build and you are already specifying it. Building a cross compiler is done when configuring LLVM itself via the cmake flag -DLLVM_TARGETS_TO_BUILD= this is what determines the default/only target supported. There might be another LLVM_xxx cmake flag to specify the default BTW.

@anonymix007
Copy link
Author

anonymix007 commented Mar 25, 2024

Runtime is libgnat_llvm.a, right? AFAIR it is for x86 currently, so will something like make gnatlib CFLAGS="-O2 --target=..." will build it for the required target?

I'm using Android LLVM fork with a few patches from this repo applied (+ cherry-picks to fix build because it's 16.0.2), which is already the cross-compiler.

@ArnaudCharlet
Copy link
Member

ArnaudCharlet commented Mar 25, 2024 via email

@anonymix007
Copy link
Author

I don't know where libgnat_llvm.a comes from, I suspect a customization on your end.

Nope, it's probably the compiler itself.

$ readelf -h libgnat.a | grep Machine
  Machine:                           Advanced Micro Devices X86-64

@anonymix007
Copy link
Author

Uploaded changes to gnat-llvm, llvm_android, llvm-project

I'm currently working on a minimal reproducible example of the issue.

@anonymix007
Copy link
Author

anonymix007 commented Mar 25, 2024

Uploaded to libmsg
It builds just fine for host (Linux, x86-64) with gcc-ada from system repos using build-linux-ada.sh script once I rename libgnat_pic.a to just libgnat.a in /lib/gcc/x86_64-pc-linux-gnu/13.2.1/adalib. This is either GCC or ArchLinux packaging bug, I guess.

First issue I see is that llvm-gnatmake doesn't support --target option, but rather -target, which in turn isn't actually supported by llvm-gnat1. Using just llvm-gcc allows to build message.o, which is already good.
In order to make libmessage.so I've tried to run ld.lld -shared message.o -o android/libmessage.so, but it produced exactly those errors:

ld.lld: error: relocation R_AARCH64_ADD_ABS_LO12_NC cannot be used against symbol 'program_error'; recompile with -fPIC
>>> defined in message.o
>>> referenced by a-conhel.adb:0 (/path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adainclude/a-conhel.adb:0)
>>>               message.o:(message__queue__implementation__te_check)

ld.lld: error: relocation R_AARCH64_ADR_PREL_PG_HI21 cannot be used against symbol 'system__soft_links__abort_defer'; recompile with -fPIC
>>> defined in message.o
>>> referenced by a-convec.adb:65 (/path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adainclude/a-convec.adb:65)
>>>               message.o:(message__queue__Oconcat__R908b___finalizer)

ld.lld: error: relocation R_AARCH64_LDST64_ABS_LO12_NC cannot be used against symbol 'system__soft_links__abort_defer'; recompile with -fPIC
>>> defined in message.o
>>> referenced by a-convec.adb:65 (/path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adainclude/a-convec.adb:65)
>>>               message.o:(message__queue__Oconcat__R908b___finalizer)

ld.lld: error: relocation R_AARCH64_ADR_PREL_PG_HI21 cannot be used against symbol 'system__soft_links__abort_undefer'; recompile with -fPIC
>>> defined in message.o
>>> referenced by a-convec.adb:0 (/path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adainclude/a-convec.adb:0)
>>>               message.o:(message__queue__Oconcat__R908b___finalizer)

ld.lld: error: relocation R_AARCH64_LDST64_ABS_LO12_NC cannot be used against symbol 'system__soft_links__abort_undefer'; recompile with -fPIC
>>> defined in message.o
>>> referenced by a-convec.adb:65 (/path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adainclude/a-convec.adb:65)
>>>               message.o:(message__queue__Oconcat__R908b___finalizer)

Apparently, I'm not supposed to run linker directly (thanks GCC for these great instructions), so let's try llvm-gnatlink:

/usr/bin/ld: b~message.o: Relocations in generic ELF (EM: 183)
/usr/bin/ld: b~message.o: Relocations in generic ELF (EM: 183)
/usr/bin/ld: b~message.o: Relocations in generic ELF (EM: 183)
/usr/bin/ld: b~message.o: Relocations in generic ELF (EM: 183)
/usr/bin/ld: b~message.o: Relocations in generic ELF (EM: 183)
/usr/bin/ld: b~message.o: Relocations in generic ELF (EM: 183)
/usr/bin/ld: b~message.o: Relocations in generic ELF (EM: 183)
/usr/bin/ld: b~message.o: error adding symbols: file in wrong format
clang: error: linker command failed with exit code 1 (use -v to see invocation)
llvm-gnatlink: error when calling /path/to/llvm-toolchain/gnat-llvm/llvm-interface/bin/llvm-gcc

This is definitely because of mixing aarch64 and x86-64, I've definitely seen similar errors before.
Why it even calls host ld? Shouldn't it be using ld.lld from AOSP clang?
UPD: There is --LINK option just for that. Seems to do something more interesting now:

ld.lld: warning: /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a: archive member 'a-nbnbig.o' is neither ET_REL nor LLVM bitcode
ld.lld: warning: /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a: archive member 's-vaispe.o' is neither ET_REL nor LLVM bitcode
ld.lld: warning: /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a: archive member 's-valspe.o' is neither ET_REL nor LLVM bitcode
ld.lld: warning: /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a: archive member 's-vauspe.o' is neither ET_REL nor LLVM bitcode
ld.lld: warning: /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a: archive member 's-vs_int.o' is neither ET_REL nor LLVM bitcode
ld.lld: warning: /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a: archive member 's-vs_lli.o' is neither ET_REL nor LLVM bitcode
ld.lld: warning: /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a: archive member 's-vs_llu.o' is neither ET_REL nor LLVM bitcode
ld.lld: warning: /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a: archive member 's-vs_uns.o' is neither ET_REL nor LLVM bitcode
ld.lld: warning: /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a: archive member 's-vsllli.o' is neither ET_REL nor LLVM bitcode
ld.lld: warning: /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a: archive member 's-vslllu.o' is neither ET_REL nor LLVM bitcode
ld.lld: error: /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a(a-assert.o) is incompatible with b~message.o
ld.lld: error: /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a(a-btgbso.o) is incompatible with b~message.o
ld.lld: error: /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a(a-calari.o) is incompatible with b~message.o
ld.lld: error: /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a(a-calcon.o) is incompatible with b~message.o
ld.lld: error: /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a(a-caldel.o) is incompatible with b~message.o
ld.lld: error: /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a(a-calend.o) is incompatible with b~message.o
ld.lld: error: /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a(a-calfor.o) is incompatible with b~message.o
ld.lld: error: /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a(a-catizo.o) is incompatible with b~message.o
ld.lld: error: /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a(a-cbdlli.o) is incompatible with b~message.o
ld.lld: error: /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a(a-cbhama.o) is incompatible with b~message.o
ld.lld: error: /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a(a-cbhase.o) is incompatible with b~message.o
ld.lld: error: /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a(a-cbmutr.o) is incompatible with b~message.o
ld.lld: error: /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a(a-cborma.o) is incompatible with b~message.o
ld.lld: error: /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a(a-cborse.o) is incompatible with b~message.o
ld.lld: error: /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a(a-cbprqu.o) is incompatible with b~message.o
ld.lld: error: /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a(a-cbsyqu.o) is incompatible with b~message.o
ld.lld: error: /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a(a-cdlili.o) is incompatible with b~message.o
ld.lld: error: /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a(a-cgaaso.o) is incompatible with b~message.o
ld.lld: error: /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a(a-cgarso.o) is incompatible with b~message.o
ld.lld: error: /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a(a-cgcaso.o) is incompatible with b~message.o
ld.lld: error: too many errors emitted, stopping now (use --error-limit=0 to see all errors)
llvm-gnatlink: error when calling /path/to/llvm-toolchain/out/install/linux-x86/clang-dev/bin/ld.lld

So libgnat.a needs to be rebuilt for aarch64.

@anonymix007
Copy link
Author

anonymix007 commented Mar 25, 2024

Building libgnat.a using make gnatlib CFLAGS="-O2 --target=aarch64-linux-android29" fails:

/path/to/llvm-toolchain/out/install/linux-x86/clang-dev/bin/clang -c -x c -O2 --target=aarch64-linux-android29 -Wno-implicit-function-declaration -I/path/to/llvm-toolchain/out/install/linux-x86/clang-dev/include -I../include -DIN_RTS=1 -fexceptions -DSTANDALONE -gdwarf-aranges -I/path/to/llvm-toolchain/gnat-llvm/llvm-interface/lib/rts-native/adainclude /path/to/llvm-toolchain/gnat-llvm/llvm-interface/lib/rts-native/adainclude/initialize.c
/path/to/llvm-toolchain/gnat-llvm/llvm-interface/lib/rts-native/adainclude/init.c:2772:10: fatal error: 'sigtramp.h' file not found
#include "sigtramp.h"
         ^~~~~~~~~~~~
/path/to/llvm-toolchain/out/install/linux-x86/clang-dev/bin/clang -c -x c -O2 --target=aarch64-linux-android29 -Wno-implicit-function-declaration -I/path/to/llvm-toolchain/out/install/linux-x86/clang-dev/include -I../include -DIN_RTS=1 -fexceptions -DSTANDALONE -gdwarf-aranges -I/path/to/llvm-toolchain/gnat-llvm/llvm-interface/lib/rts-native/adainclude /path/to/llvm-toolchain/gnat-llvm/llvm-interface/lib/rts-native/adainclude/socket.c
1 error generated.
/path/to/llvm-toolchain/out/install/linux-x86/clang-dev/bin/clang -c -x c -O2 --target=aarch64-linux-android29 -Wno-implicit-function-declaration -I/path/to/llvm-toolchain/out/install/linux-x86/clang-dev/include -I../include -DIN_RTS=1 -fexceptions -DSTANDALONE -gdwarf-aranges -I/path/to/llvm-toolchain/gnat-llvm/llvm-interface/lib/rts-native/adainclude /path/to/llvm-toolchain/gnat-llvm/llvm-interface/lib/rts-native/adainclude/mkdir.c
9 warnings generated.

   compilation of init.c failed

gprbuild: *** compilation phase failed
make[2]: *** [Makefile:251: quicklib] Error 4
make[2]: Leaving directory '/path/to/llvm-toolchain/gnat-llvm/llvm-interface'
make[1]: *** [Makefile:215: gnatlib] Error 2
make[1]: Leaving directory '/path/to/llvm-toolchain/gnat-llvm/llvm-interface'
make: *** [Makefile:12: gnatlib] Error 2

Apparently, gnat_src directory needs to be in include paths. Still doesn't compile:

/path/to/llvm-toolchain/gnat-llvm/llvm-interface/lib/rts-native/adainclude/init.c:2782:30: error: no member named 'arm_pc' in 'mcontext_t'
  ((mcontext_t *) mcontext)->arm_pc += 2;

clang just picked up OS include, which is why it doesn't work.

However, this is still a GCC bug. aarch64's mcontext_t does not have arm_pc, it's just pc.

The final build command is:

make gnatlib CFLAGS="-O2 --target=aarch64-linux-android29 -fPIC -Darm_pc=pc -I$CLANG_R475365B_CUSTOM/../../../sysroots/ndk/arm64/usr/include -I$CLANG_R475365B_CUSTOM/../../../sysroots/ndk/arm64/usr/include/aarch64-linux-android"

@anonymix007
Copy link
Author

anonymix007 commented Mar 25, 2024

And now we go back to those relocations. There was an -fPIC flag during the build of libgnat.a, but ld.lld still fails with the exact same errors. There are many of them and they're repetitive (except symbol names), so showing just one:

ld.lld: error: relocation R_AARCH64_ADR_PREL_PG_HI21 cannot be used against symbol '__gl_xdr_stream'; recompile with -fPIC
>>> defined in /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a(init.o)
>>> referenced by s-stratt.adb
>>>               s-stratt.o:(system__stream_attributes__i_ad) in archive /path/to/llvm-toolchain/gnat-llvm/llvm-interface//lib/rts-native/adalib/libgnat.a

Interestingly, __gl_xdr_stream is defined in llvm-interface/gnat_src/init.c:

$ readelf -a init.o | grep __gl_xdr_stream
    51: 0000000000000040     4 OBJECT  GLOBAL DEFAULT    5 __gl_xdr_stream

And s-stratt.o has references to it (limited to first 2 lines):

$ readelf -a s-stratt.o -W | grep __gl_xdr_stream
0000000000000004  0000000c00000113 R_AARCH64_ADR_PREL_PG_HI21 0000000000000000 __gl_xdr_stream + 0
0000000000000008  0000000c0000011d R_AARCH64_LDST32_ABS_LO12_NC 0000000000000000 __gl_xdr_stream + 0

Found how it was compiled:

/path/to/llvm-toolchain/gnat-llvm/llvm-interface/bin/llvm-gcc -c -x ada -gnatA -O2 --target=aarch64-linux-android29 -fPIC -Darm_pc=pc -I/path/to/llvm-toolchain/out/install/linux-x86/clang-dev/../../../sysroots/ndk/arm64/usr/include -I/path/to/llvm-toolchain/out/install/linux-x86/clang-dev/../../../sysroots/ndk/arm64/usr/include/aarch64-linux-android -I/path/to/llvm-toolchain/gnat-llvm/llvm-interface/gnat_src -Wno-implicit-function-declaration -I/path/to/llvm-toolchain/out/install/linux-x86/clang-dev/include -nostdinc -I../adainclude -gnatg -gnatpg -gnatec=/tmp/GNAT-TEMP-000035.TMP -gnatem=/tmp/GNAT-TEMP-000048.TMP /path/to/llvm-toolchain/gnat-llvm/llvm-interface/lib/rts-native/adainclude/s-stratt.adb

Note that -fPIC flag is present.
Tried to build the following C program just for sanity check:

extern int __gl_xdr_stream;

int get_gl_xdr_stream(void) {
	return __gl_xdr_stream;
}

And it appears to produce different relocation types:

$ readelf -a test.o -W | grep __gl_xdr_stream
0000000000000000  0000000700000137 R_AARCH64_ADR_GOT_PAGE 0000000000000000 __gl_xdr_stream + 0
0000000000000004  0000000700000138 R_AARCH64_LD64_GOT_LO12_NC 0000000000000000 __gl_xdr_stream + 0
     7: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND __gl_xdr_stream

Same with Ada:
test_ada.ads:

with Interfaces.C; use Interfaces.C;

package Test_Ada is
   XDR_Stream : int;
   pragma Import (C, XDR_Stream, "__gl_xdr_stream");
   
   function Get_XDR_Stream return int
     with
       Export        => True,
       Convention    => C,
       External_Name => "get_gl_xdr_stream_ada";
end;

test_ada.adb:

with Interfaces.C; use Interfaces.C;

package body Test_Ada is
   function Get_XDR_Stream return int is
   begin
       return XDR_Stream;
   end;
end;

And these wrong relocations are back again:

$ readelf -a test_ada.o -W | grep __gl_xdr_stream
0000000000000000  0000000500000113 R_AARCH64_ADR_PREL_PG_HI21 0000000000000000 __gl_xdr_stream + 0
0000000000000004  000000050000011d R_AARCH64_LDST32_ABS_LO12_NC 0000000000000000 __gl_xdr_stream + 0
     5: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND __gl_xdr_stream

This is either llvm or gnat-llvm bug, there's literally nothing except them used here.
Actually, this is not even aarch64-specific:

$ readelf -a test_ada_x64.o -W | grep __gl_xdr_stream
0000000000000002  0000000300000002 R_X86_64_PC32          0000000000000000 __gl_xdr_stream - 4
     3: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND __gl_xdr_stream

Any suggestions on how to debug this further?

@anonymix007
Copy link
Author

LLVM IR dumps:

; ModuleID = 'test.c'
source_filename = "test.c"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-android29"

@__gl_xdr_stream = external global i32, align 4

; Function Attrs: noinline nounwind optnone uwtable
define i32 @get_gl_xdr_stream() #0 {
  %1 = load i32, ptr @__gl_xdr_stream, align 4
  ret i32 %1
}

attributes #0 = { noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+crc32,+cx16,+cx8,+fxsr,+mmx,+popcnt,+sse,+sse2,+sse3,+sse4.1,+sse4.2,+ssse3,+x87" "tune-cpu"="generic" }

!llvm.module.flags = !{!0, !1, !2, !3}
!llvm.ident = !{!4}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 8, !"PIC Level", i32 2}
!2 = !{i32 7, !"uwtable", i32 2}
!3 = !{i32 7, !"frame-pointer", i32 2}
!4 = !{!"Android (dev, NO PGO PROFILE, NO BOLT PROFILE, based on r475365b) clang version 16.0.2 (https://android.googlesource.com/toolchain/llvm-project 080f09fc86284fa4fa7ca0824d87c10c9a763a4b)"}
; ModuleID = 'test_ada.adb'
source_filename = "test_ada.adb"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-android29"

@test_ada_E = dso_local global i16 0, align 2
@__gl_xdr_stream = external dso_local global i32, align 4

define i32 @get_gl_xdr_stream_ada() {
entry:
  %0 = load i32, ptr @__gl_xdr_stream, align 4, !tbaa !2
  ret i32 %0
}

!llvm.module.flags = !{!0, !1}

!0 = !{i32 8, !"PIC Level", i32 2}
!1 = !{i32 7, !"PIE Level", i32 0}
!2 = !{!3, !3, i64 0, i64 4}
!3 = !{!4, i64 4, !"interfaces__c__int#T1"}
!4 = !{!5, i64 4, !"interfaces__c__int#TN"}
!5 = !{!6, i64 4, !"interfaces__c__TintB#TN"}
!6 = !{!"Ada Root"}

@__gl_xdr_stream = external dso_local global i32, align 4

Wow, how nice of llvm-gcc to silently ignore -fPIC.

@anonymix007
Copy link
Author

anonymix007 commented Mar 25, 2024

$ ./main
Error: dlopen failed: cannot locate symbol "_r_debug" referenced by "/data/data/com.termux/files/home/libmsg/libmessage.so"...

There are few other undefined symbols in library (after I added some system libraries to libmessage.so dependencies only these left):

nm -D libmessage.so | grep -i ' u ' | grep -v LIBC
                 U __gnat_sigtramp
                 U _r_debug

Any ideas where can I find them?

@anonymix007
Copy link
Author

anonymix007 commented Mar 26, 2024

I guess I can live without those. Exceptions will not work, but who cares. There are more serious issues.
Returning structures doesn't work. Source code is available here.
Here's what it prints on Android (some strings omitted):

/data/data/com.termux/files/home/libmsg # ./main
GetColor: hexValue: 0x00000000, color: {0x00, 0x00, 0x00, 0x00}
ColorToInt: hexValue: 0x00680000, color: {0x00, 0x68, 0x00, 0x00}
c_uint_str: value: 0x00680000
GetColor: hexValue: 0x00680000, color: {0x00, 0x68, 0x00, 0x00}
0x00680000

(R =>  0,
 G =>  104,
 B =>  0,
 A =>  0)

(R =>  0,
 G =>  104,
 B =>  0,
 A =>  0)

And on Linux:

GetColor: hexValue: 0x00000000, color: {0x00, 0x00, 0x00, 0x00}
ColorToInt: hexValue: 0x00000000, color: {0x00, 0x00, 0x00, 0x00}
c_uint_str: value: 0x00000000
GetColor: hexValue: 0x00000000, color: {0x00, 0x00, 0x00, 0x00}
0x00000000

(R =>  0,
 G =>  0,
 B =>  0,
 A =>  0)

(R =>  0,
 G =>  0,
 B =>  0,
 A =>  0)

@ArnaudCharlet please reopen. While I found what was the cause of original issue with relocations (and it's certainly gnat-llvm bug), this is most likely another one.

Edit: with hexValue of 0x12345678 it returns 0x12680000. Something is clearly wrong here.

@anonymix007
Copy link
Author

Minimal example would be:
test.adb:

with Interfaces.C; use Interfaces.C;

function Test(hexValue: unsigned) return unsigned is
    type Color is record
        r: unsigned_char;
        g: unsigned_char;
        b: unsigned_char;
        a: unsigned_char;
    end record
        with Convention => C_Pass_By_Copy;

    function Get_Color(hexValue: unsigned) return Color
        with
            Import => True,
            Convention => C,
            External_Name => "GetColor";

    function Color_To_Int(C: Color) return unsigned
        with
            Import => True,
            Convention => C,
            External_Name => "ColorToInt";

begin
    return Color_To_Int(Get_Color(hexValue));
end;

test.c:

typedef struct {
    unsigned char r;
    unsigned char g; 
    unsigned char b;
    unsigned char a;
} Color;

extern Color GetColor(unsigned int hexValue);
extern unsigned ColorToInt(Color color);

unsigned c_test(unsigned hexValue) {
	return ColorToInt(GetColor(hexValue));
}

LLVM IR Dumps:

; ModuleID = 'test.adb'
source_filename = "test.adb"
target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
target triple = "aarch64-unknown-linux-android29"

%test__color = type <{ i8, i8, i8, i8 }>

define i32 @_ada_test(i32 %hexvalue) local_unnamed_addr #0 {
entry:
  %0 = tail call %test__color @GetColor(i32 %hexvalue)
  %.fca.0.extract = extractvalue %test__color %0, 0
  %.fca.1.extract = extractvalue %test__color %0, 1
  %.fca.2.extract = extractvalue %test__color %0, 2
  %.fca.3.extract = extractvalue %test__color %0, 3
  %.sroa.4.0.insert.ext = zext i8 %.fca.3.extract to i32
  %.sroa.4.0.insert.shift = shl nuw i32 %.sroa.4.0.insert.ext, 24
  %.sroa.3.0.insert.ext = zext i8 %.fca.2.extract to i32
  %.sroa.3.0.insert.shift = shl nuw nsw i32 %.sroa.3.0.insert.ext, 16
  %.sroa.3.0.insert.insert = or i32 %.sroa.4.0.insert.shift, %.sroa.3.0.insert.shift
  %.sroa.2.0.insert.ext = zext i8 %.fca.1.extract to i32
  %.sroa.2.0.insert.shift = shl nuw nsw i32 %.sroa.2.0.insert.ext, 8
  %.sroa.2.0.insert.insert = or i32 %.sroa.3.0.insert.insert, %.sroa.2.0.insert.shift
  %.sroa.0.0.insert.ext = zext i8 %.fca.0.extract to i32
  %.sroa.0.0.insert.insert = or i32 %.sroa.2.0.insert.insert, %.sroa.0.0.insert.ext
  %1 = tail call i32 @ColorToInt(i32 %.sroa.0.0.insert.insert)
  ret i32 %1
}

declare %test__color @GetColor(i32) local_unnamed_addr #0

declare i32 @ColorToInt(i32) local_unnamed_addr #0

attributes #0 = { "target-features"="+neon,+v8a," }

!llvm.module.flags = !{!0, !1}

!0 = !{i32 8, !"PIC Level", i32 2}
!1 = !{i32 7, !"PIE Level", i32 0}
; ModuleID = 'test.c'
source_filename = "test.c"
target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
target triple = "aarch64-unknown-linux-android29"

; Function Attrs: nounwind sspstrong uwtable
define i32 @c_test(i32 noundef %0) local_unnamed_addr #0 {
  %2 = tail call i32 @GetColor(i32 noundef %0) #2
  %3 = zext i32 %2 to i64
  %4 = tail call i32 @ColorToInt(i64 %3) #2
  ret i32 %4
}

declare i32 @ColorToInt(i64) local_unnamed_addr #1

declare i32 @GetColor(i32 noundef) local_unnamed_addr #1

attributes #0 = { nounwind sspstrong uwtable "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+fix-cortex-a53-835769,+fp-armv8,+neon,+outline-atomics,+v8a" }
attributes #1 = { "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+fix-cortex-a53-835769,+fp-armv8,+neon,+outline-atomics,+v8a" }
attributes #2 = { nounwind }

!llvm.module.flags = !{!0, !1, !2, !3}
!llvm.ident = !{!4}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 8, !"PIC Level", i32 2}
!2 = !{i32 7, !"uwtable", i32 2}
!3 = !{i32 7, !"frame-pointer", i32 1}

Once again, this is not aarch64-specific. I'm getting the same errors on x86-64 (not even Android!). You may want to reproduce with build-linux-llvm-ada.sh.

@ArnaudCharlet ArnaudCharlet reopened this Mar 27, 2024
@ArnaudCharlet
Copy link
Member

I've reopened but I'm afraid this Issue is just too confused and with too many different issues mentioned to be actionable. The last one seems to be an issue with the C_Pass_By_Copy convention.

@anonymix007
Copy link
Author

anonymix007 commented Mar 27, 2024

Do you want me to split them?
Currently I found these:

  1. C_Pass_By_Copy doesn't work properly at least for returning structures. Something fishy is also going on with passing them. LoadImage segfaults literally on the first line:
typedef struct Image {
    void *data;
    int width;
    int height;
    int mipmaps;
    int format;
} Image;

Image LoadImage(const char *fileName)
{
    Image image = { 0 };
    //...
    return image;
}
type Addr is mod 2 ** Standard'Address_Size;
type Image is record
    Data: Addr;
    Width: int;
    Height: int;
    Mipmaps: int;
    Format: int;
end record
    with Convention => C_Pass_By_Copy;
function Load_Image(File_Name: Char_Array) return Image
    with
        Import => True,
        Convention => C,
        External_Name => "LoadImage";

It actually doesn't segfault in the minimal example and only prints garbage on aarch64. This structure is too big to be passed by copy as it seems, but using just C leads to the same results.
UPD As a workaround for passing issue I'm adding pragma Volatile_Full_Access(Addr);. I have no idea what it does, but with it last 2 fields are consistently 0 (as they should be).
UPD2 And for returning issue using the following glue code works (unless C_Pass_By_Copy is really needed, i.e. in those GetColor examples):

Image * __glue_LoadImage(Image *__return_storage_ptr__, char *fileName) {
    Image image = LoadImage(fileName);
    memcpy(__return_storage_ptr__, &image, sizeof(image));
    return __return_storage_ptr__;
}

It doesn't segfault anymore, but this is not a good solution.
2. __gnat_sigtramp is missing. Probably needs to be implemented for aarch64. Looks like GCC issue though.
3. _r_debug is missing. It appears to be present in linker though:

$ strings /system/bin/linker | grep -i _r_debug                             __dl__ZL16g__r_debug_mutex
__dl__r_debug
  1. -fPIC is silently ignored. Fixed in 7f9614d in my fork (please cherry-pick it from there)
  2. target_* flags are parsed incorrectly. They assume no cross-compilation. Found this during research about the __gnat_sigtramp. Attempted to fix in 7532360
  3. AOSP Clang-specific issues (c67ef99), some are due to it being 16.0.2. But others were caused by the use of libstdc++.so
  4. Related to 2 and also probably GCC issue:
gnat-llvm/llvm-interface/lib/rts-native/adainclude/init.c:2782:30: error: no member named 'arm_pc' in 'struct sigcontext'
  ((mcontext_t *) mcontext)->arm_pc += 2;

Fixed by -Darm_pc=pc in CFLAGS (though it might be broken completely, I have no idea what is this for).

@ArnaudCharlet
Copy link
Member

Can you please open new issues for the gnat llvm specific items, so 1, 4 and 5 (and repost your proposed patches there). The rest is likely not in gnat llvm control.
Thank you!

@anonymix007
Copy link
Author

#42 is probably a good place to track the C structure passing issue. Please let me know if you don't agree and I should create a separate one, otherwise I'll add a few comments there with examples.
Opened #44 for 4 and #45 for 5

Can you report 2, 3 and 7 to GCC? Since AdaCore is one of the main Ada GCC contributors, can you maybe even fix it?

6 is a gnat llvm issues though. Once you upgrade this repo for a newer LLVM (i.e. 18.0.1 for AOSP clang-r522817 or 19.0.0 for AOSP clang-r530567) it will partially be resolved, but libstdc++.so issue will not go away. What should we even do here? Maybe some kind of compile-time flag to choose which C++ library was used?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants