From: Huang Rui <vowstar@gmail.com>
Date: Tue, 10 Dec 2025 00:00:00 +0000
Subject: [PATCH] Support GCC as assembler compiler

When CMake uses GCC as the assembler compiler instead of the raw 'as',
the assembler flags need to be passed via -Wa, prefix. This patch
detects when GCC is being used and wraps the flags accordingly.
Also adds -c flag to CMAKE_ASM_COMPILE_OBJECT when using GCC to compile
without linking.

--- a/dynamorio/make/cpp2asm_support.cmake
+++ b/dynamorio/make/cpp2asm_support.cmake
@@ -215,20 +215,43 @@ if (APPLE)
     set(ASM_FLAGS "${ASM_FLAGS} -g")
   endif (DEBUG)
 elseif (UNIX)
+  # Check if CMAKE_ASM_COMPILER is GCC (not raw 'as')
+  # GCC doesn't accept raw assembler flags, they need -Wa, prefix
+  get_filename_component(ASM_COMPILER_NAME "${CMAKE_ASM_COMPILER}" NAME)
+  if ("${ASM_COMPILER_NAME}" MATCHES "gcc$" OR "${ASM_COMPILER_NAME}" MATCHES "g\\+\\+$" OR
+      "${ASM_COMPILER_NAME}" MATCHES "^.*-gcc$" OR "${ASM_COMPILER_NAME}" MATCHES "^.*-g\\+\\+$" OR
+      "${CMAKE_ASM_COMPILER_ID}" STREQUAL "GNU")
+    set(USING_GCC_AS_ASM TRUE)
+  else ()
+    set(USING_GCC_AS_ASM FALSE)
+  endif ()
   if (DR_HOST_X86)
-    set(ASM_FLAGS "-mmnemonic=intel -msyntax=intel -mnaked-reg")
-    if (X64)
-      set(ASM_FLAGS "${ASM_FLAGS} --64")
-    else (X64)
-      # putting --32 last so we fail on -mmnemonic=intel on older as, not --32
-      set(ASM_FLAGS "${ASM_FLAGS} --32")
-    endif (X64)
+    if (USING_GCC_AS_ASM)
+      set(ASM_FLAGS "-Wa,-mmnemonic=intel,-msyntax=intel,-mnaked-reg")
+      if (X64)
+        set(ASM_FLAGS "${ASM_FLAGS} -Wa,--64")
+      else (X64)
+        set(ASM_FLAGS "${ASM_FLAGS} -Wa,--32")
+      endif (X64)
+    else ()
+      set(ASM_FLAGS "-mmnemonic=intel -msyntax=intel -mnaked-reg")
+      if (X64)
+        set(ASM_FLAGS "${ASM_FLAGS} --64")
+      else (X64)
+        # putting --32 last so we fail on -mmnemonic=intel on older as, not --32
+        set(ASM_FLAGS "${ASM_FLAGS} --32")
+      endif (X64)
+    endif ()
   elseif (DR_HOST_ARM)
     # No 64-bit support yet.
     # Some tests and libgcc/arm use deprecated instructions, disable warnings.
     set(ASM_FLAGS "${ASM_FLAGS} -mfpu=neon -mno-warn-deprecated")
   endif ()
-  set(ASM_FLAGS "${ASM_FLAGS} --noexecstack")
+  if (USING_GCC_AS_ASM)
+    set(ASM_FLAGS "${ASM_FLAGS} -Wa,--noexecstack")
+  else ()
+    set(ASM_FLAGS "${ASM_FLAGS} --noexecstack")
+  endif ()
   if (DEBUG)
     set(ASM_FLAGS "${ASM_FLAGS} -g")
   endif (DEBUG)
@@ -271,16 +294,29 @@ string(REPLACE " " ";" ASM_FLAGS_LIST "${ASM_FLAGS}")
 endif (APPLE)
 if (UNIX AND NOT APPLE)
   # We require gas >= 2.18.50 for --32, --64, and the new -msyntax=intel, etc.
+  # Always check 'as' directly since GCC's --help doesn't show assembler options
   execute_process(COMMAND
-    ${CMAKE_ASM_COMPILER} --help
+    as --help
     RESULT_VARIABLE asm_result
     ERROR_VARIABLE asm_error
     OUTPUT_VARIABLE asm_out)
   if (asm_result OR asm_error)
+    # Fallback to CMAKE_ASM_COMPILER if 'as' is not found
+    execute_process(COMMAND
+      ${CMAKE_ASM_COMPILER} --help
+      RESULT_VARIABLE asm_result
+      ERROR_VARIABLE asm_error
+      OUTPUT_VARIABLE asm_out)
+  endif ()
+  if (asm_result OR asm_error)
     message(FATAL_ERROR "*** ${CMAKE_ASM_COMPILER} failed: ***\n${asm_error}")
   endif (asm_result OR asm_error)
   # turn the flags into a vector
   string(REGEX REPLACE " " ";" flags_needed "${ASM_FLAGS}")
+  # Strip -Wa, prefix from flags (used when GCC is the assembler compiler)
+  string(REGEX REPLACE "-Wa," "" flags_needed "${flags_needed}")
+  # Split comma-separated flags that were passed to -Wa,
+  string(REGEX REPLACE "," ";" flags_needed "${flags_needed}")
   # -mfpu= does not list the possibilities
   string(REGEX REPLACE "-mfpu=[a-z]*" "-mfpu" flags_needed "${flags_needed}")
   # we want "-mmnemonic=intel" to match "-mmnemonic=[att|intel]"
@@ -333,11 +369,20 @@ if (APPLE AND NOT AARCH64)
     "<NASM> ${ASM_FLAGS} -o <OBJECT> <OBJECT>.s"
     )
 elseif (UNIX OR (APPLE AND AARCH64))
-  set(CMAKE_ASM_COMPILE_OBJECT
-    "${CMAKE_CPP} ${CMAKE_CPP_FLAGS} ${rule_flags} ${rule_defs} -E <SOURCE> -o <OBJECT>.s"
-    "<CMAKE_COMMAND> -Dfile=<OBJECT>.s -P \"${cpp2asm_newline_script_path}\""
-    "<CMAKE_ASM_COMPILER> ${ASM_FLAGS} -o <OBJECT> <OBJECT>.s"
-    )
+  if (USING_GCC_AS_ASM)
+    # When using GCC as assembler, need -c flag to compile without linking
+    set(CMAKE_ASM_COMPILE_OBJECT
+      "${CMAKE_CPP} ${CMAKE_CPP_FLAGS} ${rule_flags} ${rule_defs} -E <SOURCE> -o <OBJECT>.s"
+      "<CMAKE_COMMAND> -Dfile=<OBJECT>.s -P \"${cpp2asm_newline_script_path}\""
+      "<CMAKE_ASM_COMPILER> ${ASM_FLAGS} -c -o <OBJECT> <OBJECT>.s"
+      )
+  else ()
+    set(CMAKE_ASM_COMPILE_OBJECT
+      "${CMAKE_CPP} ${CMAKE_CPP_FLAGS} ${rule_flags} ${rule_defs} -E <SOURCE> -o <OBJECT>.s"
+      "<CMAKE_COMMAND> -Dfile=<OBJECT>.s -P \"${cpp2asm_newline_script_path}\""
+      "<CMAKE_ASM_COMPILER> ${ASM_FLAGS} -o <OBJECT> <OBJECT>.s"
+      )
+  endif ()
 else ()
   # Even if we didn't preprocess we'd need our own rule since cmake doesn't
   # support ml.
