Skip to content

Conversation

@cjacek
Copy link
Contributor

@cjacek cjacek commented Apr 10, 2025

On ARM64EC, the IMAGE_SCN_GPREL flag is repurposed to indicate that section code is x86_64. This enables embedding x86_64 code within ARM64EC object files. MSVC uses this for export thunks in .exp files.

… files

On ARM64EC, the IMAGE_SCN_GPREL flag is repurposed to indicate that section
code is x86_64. This enables embedding x86_64 code within ARM64EC object
files. MSVC uses this for export thunks in .exp files.
@llvmbot
Copy link
Member

llvmbot commented Apr 10, 2025

@llvm/pr-subscribers-platform-windows

@llvm/pr-subscribers-lld-coff

Author: Jacek Caban (cjacek)

Changes

On ARM64EC, the IMAGE_SCN_GPREL flag is repurposed to indicate that section code is x86_64. This enables embedding x86_64 code within ARM64EC object files. MSVC uses this for export thunks in .exp files.


Full diff: https://github.com/llvm/llvm-project/pull/135280.diff

3 Files Affected:

  • (modified) lld/COFF/Chunks.cpp (+10)
  • (modified) lld/COFF/Chunks.h (+1-2)
  • (added) lld/test/COFF/arm64ec-x86-sec.yaml (+43)
diff --git a/lld/COFF/Chunks.cpp b/lld/COFF/Chunks.cpp
index 3494d1ba0ac02..9d376d0dea96f 100644
--- a/lld/COFF/Chunks.cpp
+++ b/lld/COFF/Chunks.cpp
@@ -61,6 +61,16 @@ SectionChunk::SectionChunk(ObjFile *f, const coff_section *h, Kind k)
     live = true;
 }
 
+MachineTypes SectionChunk::getMachine() const {
+  MachineTypes machine = file->getMachineType();
+  // On ARM64EC, the IMAGE_SCN_GPREL flag is repurposed to indicate that section
+  // code is x86_64. This enables embedding x86_64 code within ARM64EC object
+  // files. MSVC uses this for export thunks in .exp files.
+  if (isArm64EC(machine) && (header->Characteristics & IMAGE_SCN_GPREL))
+    machine = AMD64;
+  return machine;
+}
+
 // SectionChunk is one of the most frequently allocated classes, so it is
 // important to keep it as compact as possible. As of this writing, the number
 // below is the size of this class on x64 platforms.
diff --git a/lld/COFF/Chunks.h b/lld/COFF/Chunks.h
index 9f68d5a325cc5..d03a64cc6b812 100644
--- a/lld/COFF/Chunks.h
+++ b/lld/COFF/Chunks.h
@@ -253,8 +253,7 @@ class SectionChunk : public Chunk {
   size_t getSize() const { return header->SizeOfRawData; }
   ArrayRef<uint8_t> getContents() const;
   void writeTo(uint8_t *buf) const;
-
-  MachineTypes getMachine() const { return file->getMachineType(); }
+  MachineTypes getMachine() const;
 
   // Defend against unsorted relocations. This may be overly conservative.
   void sortRelocations();
diff --git a/lld/test/COFF/arm64ec-x86-sec.yaml b/lld/test/COFF/arm64ec-x86-sec.yaml
new file mode 100644
index 0000000000000..70a5fedd9f5ca
--- /dev/null
+++ b/lld/test/COFF/arm64ec-x86-sec.yaml
@@ -0,0 +1,43 @@
+# REQUIRES: aarch64, x86
+
+# RUN: yaml2obj %s -o %t.obj
+# RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o %t-loadcfg.obj
+# RUN: lld-link -machine:arm64ec -dll -noentry %t.obj %t-loadcfg.obj -out:%t.dll
+# RUN: llvm-objdump -d %t.dll | FileCheck %s
+
+# CHECK:      Disassembly of section .text:
+# CHECK-EMPTY:
+# CHECK-NEXT: 0000000180001000 <.text>:
+# CHECK-NEXT: 180001000: d503201f     nop
+# CHECK-NEXT: 180001004: d65f03c0     ret
+# CHECK-NEXT:                 ...
+# CHECK-NEXT: 180002000: e9 ff ef ff ff               jmp     0x180001004 <.text+0x4>
+# CHECK-NEXT: 180002005: c3                           retq
+# CHECK-NEXT: 180002006: cc                           int3
+
+--- !COFF
+header:
+  Machine:         IMAGE_FILE_MACHINE_ARM64EC
+  Characteristics: [  ]
+sections:
+  - Name:            .text
+    Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+    Alignment:       4
+    SectionData:     1F2003D5C0035FD6
+    SizeOfRawData:   8
+  - Name:            .text
+    Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_GPREL, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+    Alignment:       16
+    SectionData:     E900000000C3CC
+    SizeOfRawData:   7
+    Relocations:
+      - VirtualAddress:  1
+        SymbolName:      func
+        Type:            IMAGE_REL_ARM64_PAGEBASE_REL21  # interpreted as IMAGE_REL_AMD64_REL32
+symbols:
+  - Name:            func
+    Value:           4
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL

@llvmbot
Copy link
Member

llvmbot commented Apr 10, 2025

@llvm/pr-subscribers-lld

Author: Jacek Caban (cjacek)

Changes

On ARM64EC, the IMAGE_SCN_GPREL flag is repurposed to indicate that section code is x86_64. This enables embedding x86_64 code within ARM64EC object files. MSVC uses this for export thunks in .exp files.


Full diff: https://github.com/llvm/llvm-project/pull/135280.diff

3 Files Affected:

  • (modified) lld/COFF/Chunks.cpp (+10)
  • (modified) lld/COFF/Chunks.h (+1-2)
  • (added) lld/test/COFF/arm64ec-x86-sec.yaml (+43)
diff --git a/lld/COFF/Chunks.cpp b/lld/COFF/Chunks.cpp
index 3494d1ba0ac02..9d376d0dea96f 100644
--- a/lld/COFF/Chunks.cpp
+++ b/lld/COFF/Chunks.cpp
@@ -61,6 +61,16 @@ SectionChunk::SectionChunk(ObjFile *f, const coff_section *h, Kind k)
     live = true;
 }
 
+MachineTypes SectionChunk::getMachine() const {
+  MachineTypes machine = file->getMachineType();
+  // On ARM64EC, the IMAGE_SCN_GPREL flag is repurposed to indicate that section
+  // code is x86_64. This enables embedding x86_64 code within ARM64EC object
+  // files. MSVC uses this for export thunks in .exp files.
+  if (isArm64EC(machine) && (header->Characteristics & IMAGE_SCN_GPREL))
+    machine = AMD64;
+  return machine;
+}
+
 // SectionChunk is one of the most frequently allocated classes, so it is
 // important to keep it as compact as possible. As of this writing, the number
 // below is the size of this class on x64 platforms.
diff --git a/lld/COFF/Chunks.h b/lld/COFF/Chunks.h
index 9f68d5a325cc5..d03a64cc6b812 100644
--- a/lld/COFF/Chunks.h
+++ b/lld/COFF/Chunks.h
@@ -253,8 +253,7 @@ class SectionChunk : public Chunk {
   size_t getSize() const { return header->SizeOfRawData; }
   ArrayRef<uint8_t> getContents() const;
   void writeTo(uint8_t *buf) const;
-
-  MachineTypes getMachine() const { return file->getMachineType(); }
+  MachineTypes getMachine() const;
 
   // Defend against unsorted relocations. This may be overly conservative.
   void sortRelocations();
diff --git a/lld/test/COFF/arm64ec-x86-sec.yaml b/lld/test/COFF/arm64ec-x86-sec.yaml
new file mode 100644
index 0000000000000..70a5fedd9f5ca
--- /dev/null
+++ b/lld/test/COFF/arm64ec-x86-sec.yaml
@@ -0,0 +1,43 @@
+# REQUIRES: aarch64, x86
+
+# RUN: yaml2obj %s -o %t.obj
+# RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o %t-loadcfg.obj
+# RUN: lld-link -machine:arm64ec -dll -noentry %t.obj %t-loadcfg.obj -out:%t.dll
+# RUN: llvm-objdump -d %t.dll | FileCheck %s
+
+# CHECK:      Disassembly of section .text:
+# CHECK-EMPTY:
+# CHECK-NEXT: 0000000180001000 <.text>:
+# CHECK-NEXT: 180001000: d503201f     nop
+# CHECK-NEXT: 180001004: d65f03c0     ret
+# CHECK-NEXT:                 ...
+# CHECK-NEXT: 180002000: e9 ff ef ff ff               jmp     0x180001004 <.text+0x4>
+# CHECK-NEXT: 180002005: c3                           retq
+# CHECK-NEXT: 180002006: cc                           int3
+
+--- !COFF
+header:
+  Machine:         IMAGE_FILE_MACHINE_ARM64EC
+  Characteristics: [  ]
+sections:
+  - Name:            .text
+    Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+    Alignment:       4
+    SectionData:     1F2003D5C0035FD6
+    SizeOfRawData:   8
+  - Name:            .text
+    Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_GPREL, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+    Alignment:       16
+    SectionData:     E900000000C3CC
+    SizeOfRawData:   7
+    Relocations:
+      - VirtualAddress:  1
+        SymbolName:      func
+        Type:            IMAGE_REL_ARM64_PAGEBASE_REL21  # interpreted as IMAGE_REL_AMD64_REL32
+symbols:
+  - Name:            func
+    Value:           4
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL

@cjacek
Copy link
Contributor Author

cjacek commented Apr 10, 2025

I first noticed this feature in .exp files generated by the MSVC linker when building DLLs. It appears to be useful for embedding simple x86_64 assembly code without needing to create standalone x86_64 object files. I'm considering using it for __guard_dispatch_icall_dummy in mingw-w64. However, this would also require assembler support to set the appropriate IMAGE_SCN_GPREL characteristics flag. An alternative approach would be to extend the mingw-w64 build system to build selected files as x86_64 and move __guard_dispatch_icall_dummy into one of those.

In any case, that part seems worth having in LLD for completeness.

Copy link
Member

@mstorsjo mstorsjo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. This is quite a tricky concept, especially wrt relocations being interpreted according to another standard. (Although that's only an issue with the yaml representation.) The implementation looks good!

@cjacek cjacek merged commit f8f01b5 into llvm:main Apr 11, 2025
13 of 15 checks passed
@cjacek cjacek deleted the arm64ec-x86-sec branch April 11, 2025 14:13
@llvm-ci
Copy link
Collaborator

llvm-ci commented Apr 11, 2025

LLVM Buildbot has detected a new failure on builder lldb-arm-ubuntu running on linaro-lldb-arm-ubuntu while building lld at step 6 "test".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/18/builds/14354

Here is the relevant piece of the build log for the reference
Step 6 (test) failure: build (failure)
...
PASS: lldb-api :: test_utils/TestDecorators.py (1143 of 2953)
PASS: lldb-api :: test_utils/TestInlineTest.py (1144 of 2953)
PASS: lldb-api :: test_utils/TestPExpectTest.py (1145 of 2953)
PASS: lldb-api :: test_utils/base/TestBaseTest.py (1146 of 2953)
PASS: lldb-api :: python_api/watchpoint/watchlocation/TestTargetWatchAddress.py (1147 of 2953)
PASS: lldb-api :: terminal/TestEditline.py (1148 of 2953)
UNSUPPORTED: lldb-api :: tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py (1149 of 2953)
PASS: lldb-api :: tools/lldb-dap/breakpoint/TestDAP_breakpointLocations.py (1150 of 2953)
PASS: lldb-api :: tools/lldb-dap/attach/TestDAP_attachByPortNum.py (1151 of 2953)
PASS: lldb-api :: tools/lldb-dap/breakpoint/TestDAP_logpoints.py (1152 of 2953)
FAIL: lldb-api :: tools/lldb-dap/attach/TestDAP_attach.py (1153 of 2953)
******************** TEST 'lldb-api :: tools/lldb-dap/attach/TestDAP_attach.py' FAILED ********************
Script:
--
/usr/bin/python3.10 /home/tcwg-buildbot/worker/lldb-arm-ubuntu/llvm-project/lldb/test/API/dotest.py -u CXXFLAGS -u CFLAGS --env LLVM_LIBS_DIR=/home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/./lib --env LLVM_INCLUDE_DIR=/home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/include --env LLVM_TOOLS_DIR=/home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/./bin --arch armv8l --build-dir /home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/lldb-test-build.noindex --lldb-module-cache-dir /home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/lldb-test-build.noindex/module-cache-lldb/lldb-api --clang-module-cache-dir /home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/lldb-test-build.noindex/module-cache-clang/lldb-api --executable /home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/./bin/lldb --compiler /home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/./bin/clang --dsymutil /home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/./bin/dsymutil --make /usr/bin/gmake --llvm-tools-dir /home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/./bin --lldb-obj-root /home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/tools/lldb --lldb-libs-dir /home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/./lib /home/tcwg-buildbot/worker/lldb-arm-ubuntu/llvm-project/lldb/test/API/tools/lldb-dap/attach -p TestDAP_attach.py
--
Exit Code: 1

Command Output (stdout):
--
lldb version 21.0.0git (https://github.com/llvm/llvm-project.git revision f8f01b5eeb61999e6bb9e8c52435f8ff08d410e7)
  clang revision f8f01b5eeb61999e6bb9e8c52435f8ff08d410e7
  llvm revision f8f01b5eeb61999e6bb9e8c52435f8ff08d410e7
Skipping the following test categories: ['libc++', 'dsym', 'gmodules', 'debugserver', 'objc']

--
Command Output (stderr):
--
========= DEBUG ADAPTER PROTOCOL LOGS =========
1744382432.520937920 --> (stdin/stdout) {"command":"initialize","type":"request","arguments":{"adapterID":"lldb-native","clientID":"vscode","columnsStartAt1":true,"linesStartAt1":true,"locale":"en-us","pathFormat":"path","supportsRunInTerminalRequest":true,"supportsVariablePaging":true,"supportsVariableType":true,"supportsStartDebuggingRequest":true,"supportsProgressReporting":true,"$__lldb_sourceInitFile":false},"seq":1}
1744382432.533007145 <-- (stdin/stdout) {"body":{"$__lldb_version":"lldb version 21.0.0git (https://github.com/llvm/llvm-project.git revision f8f01b5eeb61999e6bb9e8c52435f8ff08d410e7)\n  clang revision f8f01b5eeb61999e6bb9e8c52435f8ff08d410e7\n  llvm revision f8f01b5eeb61999e6bb9e8c52435f8ff08d410e7","completionTriggerCharacters":["."," ","\t"],"exceptionBreakpointFilters":[{"default":false,"filter":"cpp_catch","label":"C++ Catch"},{"default":false,"filter":"cpp_throw","label":"C++ Throw"},{"default":false,"filter":"objc_catch","label":"Objective-C Catch"},{"default":false,"filter":"objc_throw","label":"Objective-C Throw"}],"supportTerminateDebuggee":true,"supportsBreakpointLocationsRequest":true,"supportsCompletionsRequest":true,"supportsConditionalBreakpoints":true,"supportsConfigurationDoneRequest":true,"supportsDataBreakpoints":true,"supportsDelayedStackTraceLoading":true,"supportsDisassembleRequest":true,"supportsEvaluateForHovers":true,"supportsExceptionInfoRequest":true,"supportsExceptionOptions":true,"supportsFunctionBreakpoints":true,"supportsHitConditionalBreakpoints":true,"supportsInstructionBreakpoints":true,"supportsLogPoints":true,"supportsModulesRequest":true,"supportsReadMemoryRequest":true,"supportsRestartRequest":true,"supportsSetVariable":true,"supportsStepInTargetsRequest":true,"supportsSteppingGranularity":true,"supportsValueFormattingOptions":true},"command":"initialize","request_seq":1,"seq":0,"success":true,"type":"response"}
1744382432.542051554 --> (stdin/stdout) {"command":"attach","type":"request","arguments":{"program":"/tmp/lit-tmp-v43obly3/tmpxo8ulwgz","initCommands":["settings clear -all","settings set symbols.enable-external-lookup false","settings set target.inherit-tcc true","settings set target.disable-aslr false","settings set target.detach-on-error false","settings set target.auto-apply-fixits false","settings set plugin.process.gdb-remote.packet-timeout 60","settings set symbols.clang-modules-cache-path \"/home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/lldb-test-build.noindex/module-cache-lldb/lldb-api\"","settings set use-color false","settings set show-statusline false"]},"seq":2}
1744382432.542728662 <-- (stdin/stdout) {"body":{"category":"console","output":"Running initCommands:\n"},"event":"output","seq":0,"type":"event"}
1744382432.542775631 <-- (stdin/stdout) {"body":{"category":"console","output":"(lldb) settings clear -all\n"},"event":"output","seq":0,"type":"event"}
1744382432.542800188 <-- (stdin/stdout) {"body":{"category":"console","output":"(lldb) settings set symbols.enable-external-lookup false\n"},"event":"output","seq":0,"type":"event"}
1744382432.542814970 <-- (stdin/stdout) {"body":{"category":"console","output":"(lldb) settings set target.inherit-tcc true\n"},"event":"output","seq":0,"type":"event"}
1744382432.542830229 <-- (stdin/stdout) {"body":{"category":"console","output":"(lldb) settings set target.disable-aslr false\n"},"event":"output","seq":0,"type":"event"}
1744382432.542842388 <-- (stdin/stdout) {"body":{"category":"console","output":"(lldb) settings set target.detach-on-error false\n"},"event":"output","seq":0,"type":"event"}
1744382432.542855263 <-- (stdin/stdout) {"body":{"category":"console","output":"(lldb) settings set target.auto-apply-fixits false\n"},"event":"output","seq":0,"type":"event"}
1744382432.542867422 <-- (stdin/stdout) {"body":{"category":"console","output":"(lldb) settings set plugin.process.gdb-remote.packet-timeout 60\n"},"event":"output","seq":0,"type":"event"}
1744382432.542881727 <-- (stdin/stdout) {"body":{"category":"console","output":"(lldb) settings set symbols.clang-modules-cache-path \"/home/tcwg-buildbot/worker/lldb-arm-ubuntu/build/lldb-test-build.noindex/module-cache-lldb/lldb-api\"\n"},"event":"output","seq":0,"type":"event"}
1744382432.542926788 <-- (stdin/stdout) {"body":{"category":"console","output":"(lldb) settings set use-color false\n"},"event":"output","seq":0,"type":"event"}
1744382432.542940140 <-- (stdin/stdout) {"body":{"category":"console","output":"(lldb) settings set show-statusline false\n"},"event":"output","seq":0,"type":"event"}
1744382432.887336254 <-- (stdin/stdout) {"command":"attach","request_seq":2,"seq":0,"success":true,"type":"response"}
1744382432.887549877 <-- (stdin/stdout) {"body":{"isLocalProcess":true,"name":"/tmp/lit-tmp-v43obly3/tmpxo8ulwgz","startMethod":"attach","systemProcessId":1580540},"event":"process","seq":0,"type":"event"}
1744382432.887570381 <-- (stdin/stdout) {"event":"initialized","seq":0,"type":"event"}
1744382432.889030218 --> (stdin/stdout) {"command":"setBreakpoints","type":"request","arguments":{"source":{"name":"main.c","path":"main.c"},"sourceModified":false,"lines":[28],"breakpoints":[{"line":28}]},"seq":3}
1744382432.897741318 <-- (stdin/stdout) {"body":{"breakpoints":[{"column":3,"id":1,"instructionReference":"0x7D07D8","line":28,"source":{"name":"main.c","path":"main.c"},"verified":true}]},"command":"setBreakpoints","request_seq":3,"seq":0,"success":true,"type":"response"}
1744382432.897849321 <-- (stdin/stdout) {"body":{"breakpoint":{"column":3,"id":1,"instructionReference":"0x7D07D8","line":28,"verified":true},"reason":"changed"},"event":"breakpoint","seq":0,"type":"event"}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants