import java.io.*;
import java.lang.foreign.*;
import java.lang.invoke.*;

public class SigTrampolineCheck{

  private static final MemorySegment ptrStrlen;
  private static final MethodHandle strlen;
  private static final MethodHandle dladdr;
  private static final MemoryLayout structDLInfo;
  private static final VarHandle hndDliFname;

  static{
    var linker = Linker.nativeLinker();
    ptrStrlen = linker.defaultLookup()
                      .findOrThrow("strlen");
    strlen = linker.downcallHandle(
      ptrStrlen,
      FunctionDescriptor.of(linker.canonicalLayouts().get("size_t"), ValueLayout.ADDRESS)
    );
    dladdr = linker.downcallHandle(
      linker.defaultLookup().findOrThrow("dladdr"),
      FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.ADDRESS)
    );

    structDLInfo = MemoryLayout.structLayout(
      ValueLayout.ADDRESS.withName("dli_fname"),
      ValueLayout.ADDRESS.withName("dli_fbase"),
      ValueLayout.ADDRESS.withName("dli_sname"),
      ValueLayout.ADDRESS.withName("dli_saddr")
    ).withName("Dl_info");
    hndDliFname = structDLInfo.varHandle(MemoryLayout.PathElement.groupElement("dli_fname"));
  }

  private static String getLibCPath() throws Throwable{
    try(var arena = Arena.ofConfined()){
      var info = arena.allocate(structDLInfo);
      int result = (int)dladdr.invoke(ptrStrlen, info);
      if(result == 0){
        throw new RuntimeException("dladdr() returns zero");
      }

      var ptrDliFname = (MemorySegment)hndDliFname.get(info, 0);
      var libcPathLen = (long)strlen.invoke(ptrDliFname);
      return ptrDliFname.reinterpret(libcPathLen + 1) // +1 for NUL
                        .getString(0);
    }
  }

  private static boolean isSymbolAvailable(String libpath, String symbol) throws IOException{
    var proc = (new ProcessBuilder("nm", libpath)).start();
    try (var reader = proc.inputReader()) {
      return reader.lines()
                   .anyMatch(l -> l.endsWith(" " + symbol));
    }
  }

  public static void main(String[] args) throws Throwable{
    var libc = getLibCPath();
    IO.println("libc: " + libc);

    if(isSymbolAvailable(libc, "__restore_rt")){
      IO.println("__restore_rt is available");
    }
  }

}
