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

[LLD] [COFF] Fix linking MSVC generated implib header objects #122811

Merged
merged 1 commit into from
Jan 21, 2025

Conversation

mstorsjo
Copy link
Member

@mstorsjo mstorsjo commented Jan 13, 2025

ecb5ea6 tried to fix cases when LLD links what seems to be import library header objects from MSVC. However, the fix seems incorrect; the review at https://reviews.llvm.org/D133627 concluded that if this (treating this kind of symbol as a common symbol) is what link.exe does, it's fine.

However, this is most probably not what link.exe does. The symbol mentioned in the commit message of ecb5ea6 would be a common symbol with a size of around 3 GB; this is not what might have been intended.

That commit tried to avoid running into the error ".idata$4 should not refer to special section 0"; that issue is fixed for a similar style of section symbols in 4a4a8a1.

Therefore, revert ecb5ea6 and extend the fix from 4a4a8a1 to also work for the section symbols in MSVC generated import libraries.

The main detail about them, is that for symbols of type IMAGE_SYM_CLASS_SECTION, the Value field is not an offset, but it is an optional set of flags, corresponding to the Characteristics of the section header (although it may be empty).

@llvmbot
Copy link
Member

llvmbot commented Jan 13, 2025

@llvm/pr-subscribers-lld-coff

@llvm/pr-subscribers-platform-windows

Author: Martin Storsjö (mstorsjo)

Changes

ecb5ea6 tried to fix cases when LLD links what seems to be import library header objects from MSVC. However, the fix seems incorrect; the review at https://reviews.llvm.org/D133627 concluded that if this (treating this kind of symbol as a common symbol) is what link.exe does, it's fine.

However, this is most probably not what link.exe does. The symbol mentioned in the commit message of ecb5ea6 would be a common symbol with a size of around 3 GB; this is not what might have been intended.

That commit tried to avoid running into the error ".idata$4 should not refer to special section 0"; that issue is fixed for a similar style of section symbols in #122806.

Therefore, revert ecb5ea6 and extend the fix from #122806 to also work for the section symbols in MSVC generated import libraries.

The primary strange thing about them, is that the section symbols have the value 3221225536, 0xC0000040. This value matches the Windows status code STATUS_SECTION_TOO_BIG.

If interpreted literally, this value would mean that the symbols refer to an offset of 3 GB after the start of a section. Therefore, this seemingly bogus offset needs to be handled somehow.

GNU binutils also does support linking against MSVC generated import libraries - I wonder how they do it. Do they ignore the symbol Value field for symbols of type IMAGE_SYM_CLASS_SECTION? The current hacky solution in this commit is not correct as is though.

Finally - for testing, I'm constructing the header/trailer object files from .yaml, but it's hard to construct a full proper import library that way, as the short import library files for the individual symbols can't be synthesized from .yaml yet. (And even if we could do that, it's a bit tricky to build a static library with all members named e.g. "foo.dll".)


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

8 Files Affected:

  • (modified) lld/COFF/InputFiles.cpp (+20)
  • (modified) lld/COFF/Symbols.h (+3-1)
  • (added) lld/test/COFF/Inputs/msvc-implib1.yaml (+75)
  • (added) lld/test/COFF/Inputs/msvc-implib2.yaml (+24)
  • (added) lld/test/COFF/Inputs/msvc-implib3.yaml (+29)
  • (added) lld/test/COFF/wholearchive-implib.s (+55)
  • (modified) llvm/include/llvm/Object/COFF.h (+6-2)
  • (removed) llvm/test/Object/coff-sec-sym.test (-20)
diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp
index a94c984cfd4870..86284f042fe7a3 100644
--- a/lld/COFF/InputFiles.cpp
+++ b/lld/COFF/InputFiles.cpp
@@ -746,6 +746,26 @@ std::optional<Symbol *> ObjFile::createDefined(
   if (sectionNumber == llvm::COFF::IMAGE_SYM_DEBUG)
     return nullptr;
 
+  if (sym.isEmptySectionDeclaration()) {
+    // As there is no coff_section in the object file for these, make a
+    // new virtual one, with everything zeroed out (i.e. an empty section),
+    // with only the name and characteristics set.
+    StringRef name = getName();
+    auto *hdr = make<coff_section>();
+    memset(hdr, 0, sizeof(*hdr));
+    strncpy(hdr->Name, name.data(),
+            std::min(name.size(), (size_t)COFF::NameSize));
+    // We have no idea what characteristics should be assumed here; pick
+    // a default. This matches what is used for .idata sections in the regular
+    // object files in import libraries.
+    hdr->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ |
+                           IMAGE_SCN_MEM_WRITE;
+    auto *sc = make<SectionChunk>(this, hdr);
+    chunks.push_back(sc);
+    return make<DefinedRegular>(this, /*Name*/ "", /*IsCOMDAT*/ false,
+                                /*IsExternal*/ false, sym.getGeneric(), sc);
+  }
+
   if (llvm::COFF::isReservedSectionNumber(sectionNumber))
     Fatal(ctx) << toString(this) << ": " << getName()
                << " should not refer to special section "
diff --git a/lld/COFF/Symbols.h b/lld/COFF/Symbols.h
index 465d4df52c6300..6e8a9a92283530 100644
--- a/lld/COFF/Symbols.h
+++ b/lld/COFF/Symbols.h
@@ -219,7 +219,9 @@ class DefinedRegular : public DefinedCOFF {
     return s->kind() == DefinedRegularKind;
   }
 
-  uint64_t getRVA() const { return (*data)->getRVA() + sym->Value; }
+  uint64_t getRVA() const {
+    return (*data)->getRVA() + (sym->Value != 0xC0000040 ? sym->Value : 0);
+  }
   SectionChunk *getChunk() const { return *data; }
   uint32_t getValue() const { return sym->Value; }
 
diff --git a/lld/test/COFF/Inputs/msvc-implib1.yaml b/lld/test/COFF/Inputs/msvc-implib1.yaml
new file mode 100644
index 00000000000000..874913b527e406
--- /dev/null
+++ b/lld/test/COFF/Inputs/msvc-implib1.yaml
@@ -0,0 +1,75 @@
+--- !COFF
+header:
+  Machine:         IMAGE_FILE_MACHINE_AMD64
+  Characteristics: [  ]
+sections:
+  - Name:            '.idata$2'
+    Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+    Alignment:       4
+    SectionData:     '0000000000000000000000000000000000000000'
+    SizeOfRawData:   20
+    Relocations:
+      - VirtualAddress:  12
+        SymbolName:      '.idata$6'
+        Type:            IMAGE_REL_AMD64_ADDR32NB
+      - VirtualAddress:  0
+        SymbolName:      '.idata$4'
+        Type:            IMAGE_REL_AMD64_ADDR32NB
+      - VirtualAddress:  16
+        SymbolName:      '.idata$5'
+        Type:            IMAGE_REL_AMD64_ADDR32NB
+  - Name:            '.idata$6'
+    Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+    Alignment:       2
+    SectionData:     666F6F2E646C6C00
+    SizeOfRawData:   8
+symbols:
+  - Name:            '@comp.id'
+    Value:           16877185
+    SectionNumber:   -1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+  - Name:            __IMPORT_DESCRIPTOR_foo
+    Value:           0
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+  - Name:            '.idata$2'
+    Value:           3221225536
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_SECTION
+  - Name:            '.idata$6'
+    Value:           0
+    SectionNumber:   2
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+  - Name:            '.idata$4'
+    Value:           3221225536
+    SectionNumber:   0
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_SECTION
+  - Name:            '.idata$5'
+    Value:           3221225536
+    SectionNumber:   0
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_SECTION
+  - Name:            __NULL_IMPORT_DESCRIPTOR
+    Value:           0
+    SectionNumber:   0
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+  - Name:            "�foo_NULL_THUNK_DATA"
+    Value:           0
+    SectionNumber:   0
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/lld/test/COFF/Inputs/msvc-implib2.yaml b/lld/test/COFF/Inputs/msvc-implib2.yaml
new file mode 100644
index 00000000000000..50cb1df842f9cd
--- /dev/null
+++ b/lld/test/COFF/Inputs/msvc-implib2.yaml
@@ -0,0 +1,24 @@
+--- !COFF
+header:
+  Machine:         IMAGE_FILE_MACHINE_AMD64
+  Characteristics: [  ]
+sections:
+  - Name:            '.idata$3'
+    Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+    Alignment:       4
+    SectionData:     '0000000000000000000000000000000000000000'
+    SizeOfRawData:   20
+symbols:
+  - Name:            '@comp.id'
+    Value:           16877185
+    SectionNumber:   -1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+  - Name:            __NULL_IMPORT_DESCRIPTOR
+    Value:           0
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/lld/test/COFF/Inputs/msvc-implib3.yaml b/lld/test/COFF/Inputs/msvc-implib3.yaml
new file mode 100644
index 00000000000000..2cc78a7048f96e
--- /dev/null
+++ b/lld/test/COFF/Inputs/msvc-implib3.yaml
@@ -0,0 +1,29 @@
+--- !COFF
+header:
+  Machine:         IMAGE_FILE_MACHINE_AMD64
+  Characteristics: [  ]
+sections:
+  - Name:            '.idata$5'
+    Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+    Alignment:       8
+    SectionData:     '0000000000000000'
+    SizeOfRawData:   8
+  - Name:            '.idata$4'
+    Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+    Alignment:       8
+    SectionData:     '0000000000000000'
+    SizeOfRawData:   8
+symbols:
+  - Name:            '@comp.id'
+    Value:           16877185
+    SectionNumber:   -1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+  - Name:            "�foo_NULL_THUNK_DATA"
+    Value:           0
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/lld/test/COFF/wholearchive-implib.s b/lld/test/COFF/wholearchive-implib.s
new file mode 100644
index 00000000000000..4611d778316c42
--- /dev/null
+++ b/lld/test/COFF/wholearchive-implib.s
@@ -0,0 +1,55 @@
+// REQUIRES: x86
+// RUN: split-file %s %t.dir
+// RUN: llvm-lib -machine:amd64 -out:%t.lib -def:%t.dir/lib.def
+// RUN: llvm-mc -filetype=obj -triple=x86_64-windows %t.dir/main.s -o %t.main.obj
+
+// RUN: yaml2obj %S/Inputs/msvc-implib1.yaml -o %t.msvc-implib1.obj
+// RUN: yaml2obj %S/Inputs/msvc-implib2.yaml -o %t.msvc-implib2.obj
+// RUN: yaml2obj %S/Inputs/msvc-implib3.yaml -o %t.msvc-implib3.obj
+// RUN: llvm-lib -out:%t-msvc.lib %t.msvc-implib1.obj %t.msvc-implib2.obj %t.msvc-implib3.obj
+// RUN: llvm-mc -filetype=obj -triple=x86_64-windows %t.dir/main-nocall.s -o %t.main-nocall.obj
+
+// RUN: lld-link -out:%t.exe %t.main.obj -wholearchive:%t.lib -entry:entry -subsystem:console
+// RUN: llvm-readobj --coff-imports %t.exe | FileCheck %s
+
+// RUN: lld-link -out:%t-msvc.exe %t.main-nocall.obj -wholearchive:%t-msvc.lib -entry:entry -subsystem:console
+// RUN: llvm-readobj --coff-imports %t-msvc.exe | FileCheck %s --check-prefix=CHECK-MSVC
+
+// As LLD usually doesn't use the header/trailer object files from import
+// libraries, but instead synthesizes those structures, we end up with two
+// import directory entries if we force those objects to be included.
+
+// CHECK:      Import {
+// CHECK-NEXT:   Name: lib.dll
+// CHECK-NEXT:   ImportLookupTableRVA: 0x2050
+// CHECK-NEXT:   ImportAddressTableRVA: 0x2070
+// CHECK-NEXT: }
+// CHECK-NEXT: Import {
+// CHECK-NEXT:   Name: lib.dll
+// CHECK-NEXT:   ImportLookupTableRVA: 0x2058
+// CHECK-NEXT:   ImportAddressTableRVA: 0x2078
+// CHECK-NEXT:   Symbol: func (0)
+// CHECK-NEXT: }
+
+// CHECK-MSVC:      Import {
+// CHECK-MSVC-NEXT:   Name: foo.dll
+// CHECK-MSVC-NEXT:   ImportLookupTableRVA: 0x2040
+// CHECK-MSVC-NEXT:   ImportAddressTableRVA: 0x2050
+// CHECK-MSVC-NEXT: }
+
+
+#--- main.s
+.global entry
+entry:
+  call func
+  ret
+
+#--- main-nocall.s
+.global entry
+entry:
+  ret
+
+#--- lib.def
+LIBRARY lib.dll
+EXPORTS
+func
diff --git a/llvm/include/llvm/Object/COFF.h b/llvm/include/llvm/Object/COFF.h
index 05b3587224c296..3d0738c4090497 100644
--- a/llvm/include/llvm/Object/COFF.h
+++ b/llvm/include/llvm/Object/COFF.h
@@ -383,8 +383,8 @@ class COFFSymbolRef {
   }
 
   bool isCommon() const {
-    return (isExternal() || isSection()) &&
-           getSectionNumber() == COFF::IMAGE_SYM_UNDEFINED && getValue() != 0;
+    return isExternal() && getSectionNumber() == COFF::IMAGE_SYM_UNDEFINED &&
+           getValue() != 0;
   }
 
   bool isUndefined() const {
@@ -392,6 +392,10 @@ class COFFSymbolRef {
            getValue() == 0;
   }
 
+  bool isEmptySectionDeclaration() const {
+    return isSection() && getSectionNumber() == COFF::IMAGE_SYM_UNDEFINED;
+  }
+
   bool isWeakExternal() const {
     return getStorageClass() == COFF::IMAGE_SYM_CLASS_WEAK_EXTERNAL;
   }
diff --git a/llvm/test/Object/coff-sec-sym.test b/llvm/test/Object/coff-sec-sym.test
deleted file mode 100644
index 0b7117250150de..00000000000000
--- a/llvm/test/Object/coff-sec-sym.test
+++ /dev/null
@@ -1,20 +0,0 @@
-# Check that section symbol (IMAGE_SYM_CLASS_SECTION) is listed as common symbol.
-
-# RUN: yaml2obj %s -o %t.obj
-# RUN: llvm-nm %t.obj | FileCheck %s
-
-# CHECK: 00000001 C foo
-
---- !COFF
-header:
-  Machine:         IMAGE_FILE_MACHINE_AMD64
-  Characteristics: [  ]
-sections:
-symbols:
-  - Name:            foo
-    Value:           1
-    SectionNumber:   0
-    SimpleType:      IMAGE_SYM_TYPE_NULL
-    ComplexType:     IMAGE_SYM_DTYPE_NULL
-    StorageClass:    IMAGE_SYM_CLASS_SECTION
-...

@llvmbot
Copy link
Member

llvmbot commented Jan 13, 2025

@llvm/pr-subscribers-lld

Author: Martin Storsjö (mstorsjo)

Changes

ecb5ea6 tried to fix cases when LLD links what seems to be import library header objects from MSVC. However, the fix seems incorrect; the review at https://reviews.llvm.org/D133627 concluded that if this (treating this kind of symbol as a common symbol) is what link.exe does, it's fine.

However, this is most probably not what link.exe does. The symbol mentioned in the commit message of ecb5ea6 would be a common symbol with a size of around 3 GB; this is not what might have been intended.

That commit tried to avoid running into the error ".idata$4 should not refer to special section 0"; that issue is fixed for a similar style of section symbols in #122806.

Therefore, revert ecb5ea6 and extend the fix from #122806 to also work for the section symbols in MSVC generated import libraries.

The primary strange thing about them, is that the section symbols have the value 3221225536, 0xC0000040. This value matches the Windows status code STATUS_SECTION_TOO_BIG.

If interpreted literally, this value would mean that the symbols refer to an offset of 3 GB after the start of a section. Therefore, this seemingly bogus offset needs to be handled somehow.

GNU binutils also does support linking against MSVC generated import libraries - I wonder how they do it. Do they ignore the symbol Value field for symbols of type IMAGE_SYM_CLASS_SECTION? The current hacky solution in this commit is not correct as is though.

Finally - for testing, I'm constructing the header/trailer object files from .yaml, but it's hard to construct a full proper import library that way, as the short import library files for the individual symbols can't be synthesized from .yaml yet. (And even if we could do that, it's a bit tricky to build a static library with all members named e.g. "foo.dll".)


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

8 Files Affected:

  • (modified) lld/COFF/InputFiles.cpp (+20)
  • (modified) lld/COFF/Symbols.h (+3-1)
  • (added) lld/test/COFF/Inputs/msvc-implib1.yaml (+75)
  • (added) lld/test/COFF/Inputs/msvc-implib2.yaml (+24)
  • (added) lld/test/COFF/Inputs/msvc-implib3.yaml (+29)
  • (added) lld/test/COFF/wholearchive-implib.s (+55)
  • (modified) llvm/include/llvm/Object/COFF.h (+6-2)
  • (removed) llvm/test/Object/coff-sec-sym.test (-20)
diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp
index a94c984cfd4870..86284f042fe7a3 100644
--- a/lld/COFF/InputFiles.cpp
+++ b/lld/COFF/InputFiles.cpp
@@ -746,6 +746,26 @@ std::optional<Symbol *> ObjFile::createDefined(
   if (sectionNumber == llvm::COFF::IMAGE_SYM_DEBUG)
     return nullptr;
 
+  if (sym.isEmptySectionDeclaration()) {
+    // As there is no coff_section in the object file for these, make a
+    // new virtual one, with everything zeroed out (i.e. an empty section),
+    // with only the name and characteristics set.
+    StringRef name = getName();
+    auto *hdr = make<coff_section>();
+    memset(hdr, 0, sizeof(*hdr));
+    strncpy(hdr->Name, name.data(),
+            std::min(name.size(), (size_t)COFF::NameSize));
+    // We have no idea what characteristics should be assumed here; pick
+    // a default. This matches what is used for .idata sections in the regular
+    // object files in import libraries.
+    hdr->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ |
+                           IMAGE_SCN_MEM_WRITE;
+    auto *sc = make<SectionChunk>(this, hdr);
+    chunks.push_back(sc);
+    return make<DefinedRegular>(this, /*Name*/ "", /*IsCOMDAT*/ false,
+                                /*IsExternal*/ false, sym.getGeneric(), sc);
+  }
+
   if (llvm::COFF::isReservedSectionNumber(sectionNumber))
     Fatal(ctx) << toString(this) << ": " << getName()
                << " should not refer to special section "
diff --git a/lld/COFF/Symbols.h b/lld/COFF/Symbols.h
index 465d4df52c6300..6e8a9a92283530 100644
--- a/lld/COFF/Symbols.h
+++ b/lld/COFF/Symbols.h
@@ -219,7 +219,9 @@ class DefinedRegular : public DefinedCOFF {
     return s->kind() == DefinedRegularKind;
   }
 
-  uint64_t getRVA() const { return (*data)->getRVA() + sym->Value; }
+  uint64_t getRVA() const {
+    return (*data)->getRVA() + (sym->Value != 0xC0000040 ? sym->Value : 0);
+  }
   SectionChunk *getChunk() const { return *data; }
   uint32_t getValue() const { return sym->Value; }
 
diff --git a/lld/test/COFF/Inputs/msvc-implib1.yaml b/lld/test/COFF/Inputs/msvc-implib1.yaml
new file mode 100644
index 00000000000000..874913b527e406
--- /dev/null
+++ b/lld/test/COFF/Inputs/msvc-implib1.yaml
@@ -0,0 +1,75 @@
+--- !COFF
+header:
+  Machine:         IMAGE_FILE_MACHINE_AMD64
+  Characteristics: [  ]
+sections:
+  - Name:            '.idata$2'
+    Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+    Alignment:       4
+    SectionData:     '0000000000000000000000000000000000000000'
+    SizeOfRawData:   20
+    Relocations:
+      - VirtualAddress:  12
+        SymbolName:      '.idata$6'
+        Type:            IMAGE_REL_AMD64_ADDR32NB
+      - VirtualAddress:  0
+        SymbolName:      '.idata$4'
+        Type:            IMAGE_REL_AMD64_ADDR32NB
+      - VirtualAddress:  16
+        SymbolName:      '.idata$5'
+        Type:            IMAGE_REL_AMD64_ADDR32NB
+  - Name:            '.idata$6'
+    Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+    Alignment:       2
+    SectionData:     666F6F2E646C6C00
+    SizeOfRawData:   8
+symbols:
+  - Name:            '@comp.id'
+    Value:           16877185
+    SectionNumber:   -1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+  - Name:            __IMPORT_DESCRIPTOR_foo
+    Value:           0
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+  - Name:            '.idata$2'
+    Value:           3221225536
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_SECTION
+  - Name:            '.idata$6'
+    Value:           0
+    SectionNumber:   2
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+  - Name:            '.idata$4'
+    Value:           3221225536
+    SectionNumber:   0
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_SECTION
+  - Name:            '.idata$5'
+    Value:           3221225536
+    SectionNumber:   0
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_SECTION
+  - Name:            __NULL_IMPORT_DESCRIPTOR
+    Value:           0
+    SectionNumber:   0
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+  - Name:            "�foo_NULL_THUNK_DATA"
+    Value:           0
+    SectionNumber:   0
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/lld/test/COFF/Inputs/msvc-implib2.yaml b/lld/test/COFF/Inputs/msvc-implib2.yaml
new file mode 100644
index 00000000000000..50cb1df842f9cd
--- /dev/null
+++ b/lld/test/COFF/Inputs/msvc-implib2.yaml
@@ -0,0 +1,24 @@
+--- !COFF
+header:
+  Machine:         IMAGE_FILE_MACHINE_AMD64
+  Characteristics: [  ]
+sections:
+  - Name:            '.idata$3'
+    Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+    Alignment:       4
+    SectionData:     '0000000000000000000000000000000000000000'
+    SizeOfRawData:   20
+symbols:
+  - Name:            '@comp.id'
+    Value:           16877185
+    SectionNumber:   -1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+  - Name:            __NULL_IMPORT_DESCRIPTOR
+    Value:           0
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/lld/test/COFF/Inputs/msvc-implib3.yaml b/lld/test/COFF/Inputs/msvc-implib3.yaml
new file mode 100644
index 00000000000000..2cc78a7048f96e
--- /dev/null
+++ b/lld/test/COFF/Inputs/msvc-implib3.yaml
@@ -0,0 +1,29 @@
+--- !COFF
+header:
+  Machine:         IMAGE_FILE_MACHINE_AMD64
+  Characteristics: [  ]
+sections:
+  - Name:            '.idata$5'
+    Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+    Alignment:       8
+    SectionData:     '0000000000000000'
+    SizeOfRawData:   8
+  - Name:            '.idata$4'
+    Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+    Alignment:       8
+    SectionData:     '0000000000000000'
+    SizeOfRawData:   8
+symbols:
+  - Name:            '@comp.id'
+    Value:           16877185
+    SectionNumber:   -1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+  - Name:            "�foo_NULL_THUNK_DATA"
+    Value:           0
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/lld/test/COFF/wholearchive-implib.s b/lld/test/COFF/wholearchive-implib.s
new file mode 100644
index 00000000000000..4611d778316c42
--- /dev/null
+++ b/lld/test/COFF/wholearchive-implib.s
@@ -0,0 +1,55 @@
+// REQUIRES: x86
+// RUN: split-file %s %t.dir
+// RUN: llvm-lib -machine:amd64 -out:%t.lib -def:%t.dir/lib.def
+// RUN: llvm-mc -filetype=obj -triple=x86_64-windows %t.dir/main.s -o %t.main.obj
+
+// RUN: yaml2obj %S/Inputs/msvc-implib1.yaml -o %t.msvc-implib1.obj
+// RUN: yaml2obj %S/Inputs/msvc-implib2.yaml -o %t.msvc-implib2.obj
+// RUN: yaml2obj %S/Inputs/msvc-implib3.yaml -o %t.msvc-implib3.obj
+// RUN: llvm-lib -out:%t-msvc.lib %t.msvc-implib1.obj %t.msvc-implib2.obj %t.msvc-implib3.obj
+// RUN: llvm-mc -filetype=obj -triple=x86_64-windows %t.dir/main-nocall.s -o %t.main-nocall.obj
+
+// RUN: lld-link -out:%t.exe %t.main.obj -wholearchive:%t.lib -entry:entry -subsystem:console
+// RUN: llvm-readobj --coff-imports %t.exe | FileCheck %s
+
+// RUN: lld-link -out:%t-msvc.exe %t.main-nocall.obj -wholearchive:%t-msvc.lib -entry:entry -subsystem:console
+// RUN: llvm-readobj --coff-imports %t-msvc.exe | FileCheck %s --check-prefix=CHECK-MSVC
+
+// As LLD usually doesn't use the header/trailer object files from import
+// libraries, but instead synthesizes those structures, we end up with two
+// import directory entries if we force those objects to be included.
+
+// CHECK:      Import {
+// CHECK-NEXT:   Name: lib.dll
+// CHECK-NEXT:   ImportLookupTableRVA: 0x2050
+// CHECK-NEXT:   ImportAddressTableRVA: 0x2070
+// CHECK-NEXT: }
+// CHECK-NEXT: Import {
+// CHECK-NEXT:   Name: lib.dll
+// CHECK-NEXT:   ImportLookupTableRVA: 0x2058
+// CHECK-NEXT:   ImportAddressTableRVA: 0x2078
+// CHECK-NEXT:   Symbol: func (0)
+// CHECK-NEXT: }
+
+// CHECK-MSVC:      Import {
+// CHECK-MSVC-NEXT:   Name: foo.dll
+// CHECK-MSVC-NEXT:   ImportLookupTableRVA: 0x2040
+// CHECK-MSVC-NEXT:   ImportAddressTableRVA: 0x2050
+// CHECK-MSVC-NEXT: }
+
+
+#--- main.s
+.global entry
+entry:
+  call func
+  ret
+
+#--- main-nocall.s
+.global entry
+entry:
+  ret
+
+#--- lib.def
+LIBRARY lib.dll
+EXPORTS
+func
diff --git a/llvm/include/llvm/Object/COFF.h b/llvm/include/llvm/Object/COFF.h
index 05b3587224c296..3d0738c4090497 100644
--- a/llvm/include/llvm/Object/COFF.h
+++ b/llvm/include/llvm/Object/COFF.h
@@ -383,8 +383,8 @@ class COFFSymbolRef {
   }
 
   bool isCommon() const {
-    return (isExternal() || isSection()) &&
-           getSectionNumber() == COFF::IMAGE_SYM_UNDEFINED && getValue() != 0;
+    return isExternal() && getSectionNumber() == COFF::IMAGE_SYM_UNDEFINED &&
+           getValue() != 0;
   }
 
   bool isUndefined() const {
@@ -392,6 +392,10 @@ class COFFSymbolRef {
            getValue() == 0;
   }
 
+  bool isEmptySectionDeclaration() const {
+    return isSection() && getSectionNumber() == COFF::IMAGE_SYM_UNDEFINED;
+  }
+
   bool isWeakExternal() const {
     return getStorageClass() == COFF::IMAGE_SYM_CLASS_WEAK_EXTERNAL;
   }
diff --git a/llvm/test/Object/coff-sec-sym.test b/llvm/test/Object/coff-sec-sym.test
deleted file mode 100644
index 0b7117250150de..00000000000000
--- a/llvm/test/Object/coff-sec-sym.test
+++ /dev/null
@@ -1,20 +0,0 @@
-# Check that section symbol (IMAGE_SYM_CLASS_SECTION) is listed as common symbol.
-
-# RUN: yaml2obj %s -o %t.obj
-# RUN: llvm-nm %t.obj | FileCheck %s
-
-# CHECK: 00000001 C foo
-
---- !COFF
-header:
-  Machine:         IMAGE_FILE_MACHINE_AMD64
-  Characteristics: [  ]
-sections:
-symbols:
-  - Name:            foo
-    Value:           1
-    SectionNumber:   0
-    SimpleType:      IMAGE_SYM_TYPE_NULL
-    ComplexType:     IMAGE_SYM_DTYPE_NULL
-    StorageClass:    IMAGE_SYM_CLASS_SECTION
-...

@llvmbot
Copy link
Member

llvmbot commented Jan 13, 2025

@llvm/pr-subscribers-llvm-binary-utilities

Author: Martin Storsjö (mstorsjo)

Changes

ecb5ea6 tried to fix cases when LLD links what seems to be import library header objects from MSVC. However, the fix seems incorrect; the review at https://reviews.llvm.org/D133627 concluded that if this (treating this kind of symbol as a common symbol) is what link.exe does, it's fine.

However, this is most probably not what link.exe does. The symbol mentioned in the commit message of ecb5ea6 would be a common symbol with a size of around 3 GB; this is not what might have been intended.

That commit tried to avoid running into the error ".idata$4 should not refer to special section 0"; that issue is fixed for a similar style of section symbols in #122806.

Therefore, revert ecb5ea6 and extend the fix from #122806 to also work for the section symbols in MSVC generated import libraries.

The primary strange thing about them, is that the section symbols have the value 3221225536, 0xC0000040. This value matches the Windows status code STATUS_SECTION_TOO_BIG.

If interpreted literally, this value would mean that the symbols refer to an offset of 3 GB after the start of a section. Therefore, this seemingly bogus offset needs to be handled somehow.

GNU binutils also does support linking against MSVC generated import libraries - I wonder how they do it. Do they ignore the symbol Value field for symbols of type IMAGE_SYM_CLASS_SECTION? The current hacky solution in this commit is not correct as is though.

Finally - for testing, I'm constructing the header/trailer object files from .yaml, but it's hard to construct a full proper import library that way, as the short import library files for the individual symbols can't be synthesized from .yaml yet. (And even if we could do that, it's a bit tricky to build a static library with all members named e.g. "foo.dll".)


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

8 Files Affected:

  • (modified) lld/COFF/InputFiles.cpp (+20)
  • (modified) lld/COFF/Symbols.h (+3-1)
  • (added) lld/test/COFF/Inputs/msvc-implib1.yaml (+75)
  • (added) lld/test/COFF/Inputs/msvc-implib2.yaml (+24)
  • (added) lld/test/COFF/Inputs/msvc-implib3.yaml (+29)
  • (added) lld/test/COFF/wholearchive-implib.s (+55)
  • (modified) llvm/include/llvm/Object/COFF.h (+6-2)
  • (removed) llvm/test/Object/coff-sec-sym.test (-20)
diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp
index a94c984cfd4870..86284f042fe7a3 100644
--- a/lld/COFF/InputFiles.cpp
+++ b/lld/COFF/InputFiles.cpp
@@ -746,6 +746,26 @@ std::optional<Symbol *> ObjFile::createDefined(
   if (sectionNumber == llvm::COFF::IMAGE_SYM_DEBUG)
     return nullptr;
 
+  if (sym.isEmptySectionDeclaration()) {
+    // As there is no coff_section in the object file for these, make a
+    // new virtual one, with everything zeroed out (i.e. an empty section),
+    // with only the name and characteristics set.
+    StringRef name = getName();
+    auto *hdr = make<coff_section>();
+    memset(hdr, 0, sizeof(*hdr));
+    strncpy(hdr->Name, name.data(),
+            std::min(name.size(), (size_t)COFF::NameSize));
+    // We have no idea what characteristics should be assumed here; pick
+    // a default. This matches what is used for .idata sections in the regular
+    // object files in import libraries.
+    hdr->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ |
+                           IMAGE_SCN_MEM_WRITE;
+    auto *sc = make<SectionChunk>(this, hdr);
+    chunks.push_back(sc);
+    return make<DefinedRegular>(this, /*Name*/ "", /*IsCOMDAT*/ false,
+                                /*IsExternal*/ false, sym.getGeneric(), sc);
+  }
+
   if (llvm::COFF::isReservedSectionNumber(sectionNumber))
     Fatal(ctx) << toString(this) << ": " << getName()
                << " should not refer to special section "
diff --git a/lld/COFF/Symbols.h b/lld/COFF/Symbols.h
index 465d4df52c6300..6e8a9a92283530 100644
--- a/lld/COFF/Symbols.h
+++ b/lld/COFF/Symbols.h
@@ -219,7 +219,9 @@ class DefinedRegular : public DefinedCOFF {
     return s->kind() == DefinedRegularKind;
   }
 
-  uint64_t getRVA() const { return (*data)->getRVA() + sym->Value; }
+  uint64_t getRVA() const {
+    return (*data)->getRVA() + (sym->Value != 0xC0000040 ? sym->Value : 0);
+  }
   SectionChunk *getChunk() const { return *data; }
   uint32_t getValue() const { return sym->Value; }
 
diff --git a/lld/test/COFF/Inputs/msvc-implib1.yaml b/lld/test/COFF/Inputs/msvc-implib1.yaml
new file mode 100644
index 00000000000000..874913b527e406
--- /dev/null
+++ b/lld/test/COFF/Inputs/msvc-implib1.yaml
@@ -0,0 +1,75 @@
+--- !COFF
+header:
+  Machine:         IMAGE_FILE_MACHINE_AMD64
+  Characteristics: [  ]
+sections:
+  - Name:            '.idata$2'
+    Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+    Alignment:       4
+    SectionData:     '0000000000000000000000000000000000000000'
+    SizeOfRawData:   20
+    Relocations:
+      - VirtualAddress:  12
+        SymbolName:      '.idata$6'
+        Type:            IMAGE_REL_AMD64_ADDR32NB
+      - VirtualAddress:  0
+        SymbolName:      '.idata$4'
+        Type:            IMAGE_REL_AMD64_ADDR32NB
+      - VirtualAddress:  16
+        SymbolName:      '.idata$5'
+        Type:            IMAGE_REL_AMD64_ADDR32NB
+  - Name:            '.idata$6'
+    Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+    Alignment:       2
+    SectionData:     666F6F2E646C6C00
+    SizeOfRawData:   8
+symbols:
+  - Name:            '@comp.id'
+    Value:           16877185
+    SectionNumber:   -1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+  - Name:            __IMPORT_DESCRIPTOR_foo
+    Value:           0
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+  - Name:            '.idata$2'
+    Value:           3221225536
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_SECTION
+  - Name:            '.idata$6'
+    Value:           0
+    SectionNumber:   2
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+  - Name:            '.idata$4'
+    Value:           3221225536
+    SectionNumber:   0
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_SECTION
+  - Name:            '.idata$5'
+    Value:           3221225536
+    SectionNumber:   0
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_SECTION
+  - Name:            __NULL_IMPORT_DESCRIPTOR
+    Value:           0
+    SectionNumber:   0
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+  - Name:            "�foo_NULL_THUNK_DATA"
+    Value:           0
+    SectionNumber:   0
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/lld/test/COFF/Inputs/msvc-implib2.yaml b/lld/test/COFF/Inputs/msvc-implib2.yaml
new file mode 100644
index 00000000000000..50cb1df842f9cd
--- /dev/null
+++ b/lld/test/COFF/Inputs/msvc-implib2.yaml
@@ -0,0 +1,24 @@
+--- !COFF
+header:
+  Machine:         IMAGE_FILE_MACHINE_AMD64
+  Characteristics: [  ]
+sections:
+  - Name:            '.idata$3'
+    Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+    Alignment:       4
+    SectionData:     '0000000000000000000000000000000000000000'
+    SizeOfRawData:   20
+symbols:
+  - Name:            '@comp.id'
+    Value:           16877185
+    SectionNumber:   -1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+  - Name:            __NULL_IMPORT_DESCRIPTOR
+    Value:           0
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/lld/test/COFF/Inputs/msvc-implib3.yaml b/lld/test/COFF/Inputs/msvc-implib3.yaml
new file mode 100644
index 00000000000000..2cc78a7048f96e
--- /dev/null
+++ b/lld/test/COFF/Inputs/msvc-implib3.yaml
@@ -0,0 +1,29 @@
+--- !COFF
+header:
+  Machine:         IMAGE_FILE_MACHINE_AMD64
+  Characteristics: [  ]
+sections:
+  - Name:            '.idata$5'
+    Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+    Alignment:       8
+    SectionData:     '0000000000000000'
+    SizeOfRawData:   8
+  - Name:            '.idata$4'
+    Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+    Alignment:       8
+    SectionData:     '0000000000000000'
+    SizeOfRawData:   8
+symbols:
+  - Name:            '@comp.id'
+    Value:           16877185
+    SectionNumber:   -1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+  - Name:            "�foo_NULL_THUNK_DATA"
+    Value:           0
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/lld/test/COFF/wholearchive-implib.s b/lld/test/COFF/wholearchive-implib.s
new file mode 100644
index 00000000000000..4611d778316c42
--- /dev/null
+++ b/lld/test/COFF/wholearchive-implib.s
@@ -0,0 +1,55 @@
+// REQUIRES: x86
+// RUN: split-file %s %t.dir
+// RUN: llvm-lib -machine:amd64 -out:%t.lib -def:%t.dir/lib.def
+// RUN: llvm-mc -filetype=obj -triple=x86_64-windows %t.dir/main.s -o %t.main.obj
+
+// RUN: yaml2obj %S/Inputs/msvc-implib1.yaml -o %t.msvc-implib1.obj
+// RUN: yaml2obj %S/Inputs/msvc-implib2.yaml -o %t.msvc-implib2.obj
+// RUN: yaml2obj %S/Inputs/msvc-implib3.yaml -o %t.msvc-implib3.obj
+// RUN: llvm-lib -out:%t-msvc.lib %t.msvc-implib1.obj %t.msvc-implib2.obj %t.msvc-implib3.obj
+// RUN: llvm-mc -filetype=obj -triple=x86_64-windows %t.dir/main-nocall.s -o %t.main-nocall.obj
+
+// RUN: lld-link -out:%t.exe %t.main.obj -wholearchive:%t.lib -entry:entry -subsystem:console
+// RUN: llvm-readobj --coff-imports %t.exe | FileCheck %s
+
+// RUN: lld-link -out:%t-msvc.exe %t.main-nocall.obj -wholearchive:%t-msvc.lib -entry:entry -subsystem:console
+// RUN: llvm-readobj --coff-imports %t-msvc.exe | FileCheck %s --check-prefix=CHECK-MSVC
+
+// As LLD usually doesn't use the header/trailer object files from import
+// libraries, but instead synthesizes those structures, we end up with two
+// import directory entries if we force those objects to be included.
+
+// CHECK:      Import {
+// CHECK-NEXT:   Name: lib.dll
+// CHECK-NEXT:   ImportLookupTableRVA: 0x2050
+// CHECK-NEXT:   ImportAddressTableRVA: 0x2070
+// CHECK-NEXT: }
+// CHECK-NEXT: Import {
+// CHECK-NEXT:   Name: lib.dll
+// CHECK-NEXT:   ImportLookupTableRVA: 0x2058
+// CHECK-NEXT:   ImportAddressTableRVA: 0x2078
+// CHECK-NEXT:   Symbol: func (0)
+// CHECK-NEXT: }
+
+// CHECK-MSVC:      Import {
+// CHECK-MSVC-NEXT:   Name: foo.dll
+// CHECK-MSVC-NEXT:   ImportLookupTableRVA: 0x2040
+// CHECK-MSVC-NEXT:   ImportAddressTableRVA: 0x2050
+// CHECK-MSVC-NEXT: }
+
+
+#--- main.s
+.global entry
+entry:
+  call func
+  ret
+
+#--- main-nocall.s
+.global entry
+entry:
+  ret
+
+#--- lib.def
+LIBRARY lib.dll
+EXPORTS
+func
diff --git a/llvm/include/llvm/Object/COFF.h b/llvm/include/llvm/Object/COFF.h
index 05b3587224c296..3d0738c4090497 100644
--- a/llvm/include/llvm/Object/COFF.h
+++ b/llvm/include/llvm/Object/COFF.h
@@ -383,8 +383,8 @@ class COFFSymbolRef {
   }
 
   bool isCommon() const {
-    return (isExternal() || isSection()) &&
-           getSectionNumber() == COFF::IMAGE_SYM_UNDEFINED && getValue() != 0;
+    return isExternal() && getSectionNumber() == COFF::IMAGE_SYM_UNDEFINED &&
+           getValue() != 0;
   }
 
   bool isUndefined() const {
@@ -392,6 +392,10 @@ class COFFSymbolRef {
            getValue() == 0;
   }
 
+  bool isEmptySectionDeclaration() const {
+    return isSection() && getSectionNumber() == COFF::IMAGE_SYM_UNDEFINED;
+  }
+
   bool isWeakExternal() const {
     return getStorageClass() == COFF::IMAGE_SYM_CLASS_WEAK_EXTERNAL;
   }
diff --git a/llvm/test/Object/coff-sec-sym.test b/llvm/test/Object/coff-sec-sym.test
deleted file mode 100644
index 0b7117250150de..00000000000000
--- a/llvm/test/Object/coff-sec-sym.test
+++ /dev/null
@@ -1,20 +0,0 @@
-# Check that section symbol (IMAGE_SYM_CLASS_SECTION) is listed as common symbol.
-
-# RUN: yaml2obj %s -o %t.obj
-# RUN: llvm-nm %t.obj | FileCheck %s
-
-# CHECK: 00000001 C foo
-
---- !COFF
-header:
-  Machine:         IMAGE_FILE_MACHINE_AMD64
-  Characteristics: [  ]
-sections:
-symbols:
-  - Name:            foo
-    Value:           1
-    SectionNumber:   0
-    SimpleType:      IMAGE_SYM_TYPE_NULL
-    ComplexType:     IMAGE_SYM_DTYPE_NULL
-    StorageClass:    IMAGE_SYM_CLASS_SECTION
-...

@pzhengqc
Copy link
Collaborator

Thanks for the patch, @mstorsjo! Even though I am not sure how MSVC or GNU binutils handles this, but your patch looks reasonable to me.

@@ -219,7 +219,9 @@ class DefinedRegular : public DefinedCOFF {
return s->kind() == DefinedRegularKind;
}

uint64_t getRVA() const { return (*data)->getRVA() + sym->Value; }
uint64_t getRVA() const {
return (*data)->getRVA() + (sym->Value != 0xC0000040 ? sym->Value : 0);
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if we could avoid special-casing that value. From what I can see with binutils, value is simply ignored for such symbols. Maybe we could just always create a symbol with 0 value when handling isEmptySectionDeclaration in lld-link.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, we most probably should avoid hardcoding it, especially like this; this was my mildly provocative PoC to show where/how we may need to work around it :-)

Indeed, ignoring the offset when handling isEmptySectionDeclaration probably is good, but that's apparently not entirely enough either. Note this section symbol:

  - Name:            '.idata$2'
    Value:           3221225536
    SectionNumber:   1
    SimpleType:      IMAGE_SYM_TYPE_NULL
    ComplexType:     IMAGE_SYM_DTYPE_NULL
    StorageClass:    IMAGE_SYM_CLASS_SECTION

So apparently we should ignore the Value entirely, for any section type symbol it seems? I haven't quite yet had time to work out the logic around this, how binutils and link.exe operate around this.

@mstorsjo
Copy link
Member Author

A couple more observations about the MSVC behaviour here. I used inputs like this:

#--- main.yaml
--- !COFF
header:
  Machine:         IMAGE_FILE_MACHINE_AMD64
  Characteristics: [  ]
sections:            
  - Name:            '.itest$2'
    Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]     
    Alignment:       4
    SectionData:     '00000000000000000000'
    SizeOfRawData:   10
    Relocations:
      - VirtualAddress:  0
        SymbolName:      '.itest$6'
        Type:            IMAGE_REL_AMD64_ADDR32NB
  - Name:            '.itest$6'
    Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]     
    Alignment:       2
    SectionData:     01000000
    SizeOfRawData:   4
symbols:
  - Name:            '.itest$2'
    Value:           0
    SectionNumber:   1
    SimpleType:      IMAGE_SYM_TYPE_NULL
    ComplexType:     IMAGE_SYM_DTYPE_NULL
    StorageClass:    IMAGE_SYM_CLASS_SECTION
  - Name:            '.itest$6'
    Value:           3221225536
    SectionNumber:   2
    SimpleType:      IMAGE_SYM_TYPE_NULL
    ComplexType:     IMAGE_SYM_DTYPE_NULL
    StorageClass:    IMAGE_SYM_CLASS_SECTION
  - Name:            '.itest$4'
    Value:           3221225536
    SectionNumber:   0
    SimpleType:      IMAGE_SYM_TYPE_NULL
    ComplexType:     IMAGE_SYM_DTYPE_NULL
    StorageClass:    IMAGE_SYM_CLASS_SECTION
...
#--- sect-def.yaml
--- !COFF
header:
  Machine:         IMAGE_FILE_MACHINE_AMD64
  Characteristics: [  ]
sections:            
  - Name:            '.itest$4'
    Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
    Alignment:       2
    SectionData:     'ffff'
    SizeOfRawData:   2
symbols:
  - Name:            '.itest$4'
    Value:           0
    SectionNumber:   1
    SimpleType:      IMAGE_SYM_TYPE_NULL
    ComplexType:     IMAGE_SYM_DTYPE_NULL
    StorageClass:    IMAGE_SYM_CLASS_SECTION
...

Compared with earlier tests, I'm adding a second yaml object for the actual .itest$4 section. I've changed .itest$6 to IMAGE_SYM_CLASS_SECTION whereas it usually seems to be IMAGE_SYM_CLASS_STATIC for .idata$6 in the case of actual import libraries. I'm converting yaml->obj and then passing both .obj to MS link.exe.

  • MS link.exe behaves weirdly with relocations against the section symbols here. Both IMAGE_REL_AMD64_ADDR32NB and IMAGE_REL_AMD64_ADDR64 seem to behave in the same way. With this example as is, I'm getting the address of the start of the .itest section, not the start of the .itest$6 chunk. Same if I point the relocation at the .itest$4 symbol. If I rename the .itest section to .idata, I get the correct address for the .itest$6 chunk, but I get a plain zero (for ADDR32NB) and the image base (for ADDR64) if I point the relocation at .itest$4. It feels like something is really odd here; I would expect that there are relocations against section symbols in many everyday object files (plus in the import library headers), so these really do need to work correctly in general. No idea what odd thing we have there that makes link.exe misbehave...

  • The Value of symbols of type IMAGE_SYM_CLASS_SECTION is not an offset, but it is the Characteristics of the section. The magic value we're seeing, 3221225536 aka 0x0xC0000040 is not STATUS_SECTION_TOO_BIG, but it is IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE. If the field is zero, it is ignored, but if not and it doesn't match (either a real section header in the same object file, or a section header in another object file), MS link.exe prints a warning like this: warning LNK4078: multiple '.idata' sections found with different attributes (40000040)

For the former issue, I'm not sure if there's much of an action we need to take; it is very distracting when trying to figure out how things really work, but I'm not sure if resolving this is relevant for this case.

For the latter issue, we should at least ignore the Value field for symbols of type IMAGE_SYM_CLASS_SECTION, instead of treating it as an offset. (We might not need to actually try to apply it as section characteristics.) I'm not sure how to handle this though... When we create a DefinedRegular for these symbols, we don't really distinguish between symbol types IMAGE_SYM_CLASS_SECTION vs e.g. IMAGE_SYM_CLASS_STATIC. And within DefinedRegular, we access them via coff_symbol_generic which doesn't see the StorageClass field.

Should we add a DefinedRegular subclass which ignores the offset? That's maybe the most efficient way around it, without needing to litter DefinedRegular with a condition in e.g. the getRVA() method. (Alternatively, DefinedRegular would need a flag about whether to ignore the Value offset or not.)

@mstorsjo mstorsjo force-pushed the lld-msvc-implib-header branch 2 times, most recently from 02789ac to 4668992 Compare January 16, 2025 13:48
@cjacek
Copy link
Contributor

cjacek commented Jan 16, 2025

Interesting findings, thanks!

For the former issue, I'm not sure if there's much of an action we need to take; it is very distracting when trying to figure out how things really work, but I'm not sure if resolving this is relevant for this case.

Agreed, sounds doesn't sounds like something we need to worry about.

Should we add a DefinedRegular subclass which ignores the offset? That's maybe the most efficient way around it, without needing to litter DefinedRegular with a condition in e.g. the getRVA() method. (Alternatively, DefinedRegular would need a flag about whether to ignore the Value offset or not.)

I guess instead of a flag, DefinedRegular could just store the offset itself, copy it from coff_symbol_generic in the constructor and use it internally instead. In this case, we could then just override it. A new symbol type works too.

@mstorsjo
Copy link
Member Author

Should we add a DefinedRegular subclass which ignores the offset? That's maybe the most efficient way around it, without needing to litter DefinedRegular with a condition in e.g. the getRVA() method. (Alternatively, DefinedRegular would need a flag about whether to ignore the Value offset or not.)

I made an attempt at this now, but adding a DefinedRegular subclass also is a bit messy; DefinedRegular::getValue() is not virtual (the whole class hierarchy in Symbols.h is quite tweaked not to use regular C++ inheritance facilities), so e.g. the offset does appear e.g. in the map file (see the updated test). Alternatively DefinedSection could inherit directly from DefinedCOFF, but then these symbols are omitted from the map file entirely (as they don't hit dyn_cast<DefinedRegular>()) - we have lots of code all over the place that operate on/around DefinedRegular, so making it not hit that may be problematic; we also have switches that operate directly on s->kind().

Or should we create a synthetic coff_symbol_generic with a zero Value?

@mstorsjo
Copy link
Member Author

Should we add a DefinedRegular subclass which ignores the offset? That's maybe the most efficient way around it, without needing to litter DefinedRegular with a condition in e.g. the getRVA() method. (Alternatively, DefinedRegular would need a flag about whether to ignore the Value offset or not.)

I guess instead of a flag, DefinedRegular could just store the offset itself, copy it from coff_symbol_generic in the constructor and use it internally instead. In this case, we could then just override it. A new symbol type works too.

Oh, that sounds like a good idea. That does increase the size of DefinedRegular by 4 bytes, and we're space conscious about it (see static_assert(sizeof(SymbolUnion) <= 48, "symbols should be optimized for memory usage"); at the head of Symbols.cpp), but it seems like I can add such a field without affecting this, at least on x86_64 Linux, so perhaps that's a viable strategy after all?

@mstorsjo mstorsjo force-pushed the lld-msvc-implib-header branch from 4668992 to dc495a0 Compare January 16, 2025 14:04
@mstorsjo
Copy link
Member Author

Or should we create a synthetic coff_symbol_generic with a zero Value?

This approach seems quite straightforward, and doesn't require adjusting the existing DefinedRegular class at all, so this looks to me like the best compromise so far.

Copy link
Contributor

@cjacek cjacek left a comment

Choose a reason for hiding this comment

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

It looks reasonable to me.

lld/COFF/InputFiles.cpp Outdated Show resolved Hide resolved
@mstorsjo mstorsjo force-pushed the lld-msvc-implib-header branch from dc495a0 to 8765e26 Compare January 17, 2025 22:51
@mstorsjo mstorsjo changed the title RFC: [LLD] [COFF] Fix linking MSVC generated implib header objects [LLD] [COFF] Fix linking MSVC generated implib header objects Jan 17, 2025
@@ -3,9 +3,18 @@
// RUN: llvm-lib -machine:amd64 -out:%t.lib -def:%t.dir/lib.def
// RUN: llvm-mc -filetype=obj -triple=x86_64-windows %t.dir/main.s -o %t.main.obj

// RUN: yaml2obj %S/Inputs/msvc-implib1.yaml -o %t.msvc-implib1.obj
Copy link
Member Author

Choose a reason for hiding this comment

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

Btw, I wonder if we really need these extra tests (they're kinda ugly with three separate .yaml files) - I think the changes in empty-section-decl.yaml are enough to cover the changes in this PR, so I could drop the rest of the added tests.

Copy link
Contributor

Choose a reason for hiding this comment

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

empty-section-decl.yaml changes seem enough to me as well.

ecb5ea6 tried to fix cases
when LLD links what seems to be import library header objects
from MSVC. However, the fix seems incorrect; the review at
https://reviews.llvm.org/D133627 concluded that if this (treating
this kind of symbol as a common symbol) is what link.exe does,
it's fine.

However, this is most probably not what link.exe does. The
symbol mentioned in the commit message of
ecb5ea6 would be a common symbol
with a size of around 3 GB; this is not what might have been
intended.

That commit tried to avoid running into the error ".idata$4 should
not refer to special section 0"; that issue is fixed for a similar
style of section symbols in
4a4a8a1.

Therefore, revert ecb5ea6 and
extend the fix from 4a4a8a1 to
also work for the section symbols in MSVC generated import libraries.

The main detail about them, is that for symbols of type
IMAGE_SYM_CLASS_SECTION, the Value field is not an offset, but
it is an optional set of flags, corresponding to the Characteristics
of the section header (although it may be empty).
@mstorsjo mstorsjo force-pushed the lld-msvc-implib-header branch from 8765e26 to 10c9ebe Compare January 20, 2025 21:48
@mstorsjo mstorsjo merged commit 9457418 into llvm:main Jan 21, 2025
8 checks passed
@mstorsjo mstorsjo deleted the lld-msvc-implib-header branch January 21, 2025 21:55
@thurstond
Copy link
Contributor

COFF/empty-section-decl.yaml started failing on some buildbots (https://lab.llvm.org/buildbot/#/builders/52/builds/5421). Could you please take a look to check if this is related?

@thurstond
Copy link
Contributor

The buildbot is missing some of the test output, but I re-ran the buildbot locally:

******************** TEST 'lld :: COFF/empty-section-decl.yaml' FAILED ********************
Exit Code: 1

Command Output (stderr):
--
RUN: at line 3: /usr/local/google/home/thurston/buildbot_repro/llvm_build_asan/bin/yaml2obj /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/test/COFF/empty-section-decl.yaml -o /usr/local/google/home/thurston/buildbot_repro/llvm_build_asan/tools/lld/test/COFF/Output/empty-section-decl.yaml.tmp.obj
+ /usr/local/google/home/thurston/buildbot_repro/llvm_build_asan/bin/yaml2obj /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/test/COFF/empty-section-decl.yaml -o /usr/local/google/home/thurston/buildbot_repro/llvm_build_asan/tools/lld/test/COFF/Output/empty-section-decl.yaml.tmp.obj
RUN: at line 4: /usr/local/google/home/thurston/buildbot_repro/llvm_build_asan/bin/lld-link -dll -out:/usr/local/google/home/thurston/buildbot_repro/llvm_build_asan/tools/lld/test/COFF/Output/empty-section-decl.yaml.tmp.dll /usr/local/google/home/thurston/buildbot_repro/llvm_build_asan/tools/lld/test/COFF/Output/empty-section-decl.yaml.tmp.obj -noentry -subsystem:console -lldmap:/usr/local/google/home/thurston/buildbot_repro/llvm_build_asan/tools/lld/test/COFF/Output/empty-section-decl.yaml.tmp.map
+ /usr/local/google/home/thurston/buildbot_repro/llvm_build_asan/bin/lld-link -dll -out:/usr/local/google/home/thurston/buildbot_repro/llvm_build_asan/tools/lld/test/COFF/Output/empty-section-decl.yaml.tmp.dll /usr/local/google/home/thurston/buildbot_repro/llvm_build_asan/tools/lld/test/COFF/Output/empty-section-decl.yaml.tmp.obj -noentry -subsystem:console -lldmap:/usr/local/google/home/thurston/buildbot_repro/llvm_build_asan/tools/lld/test/COFF/Output/empty-section-decl.yaml.tmp.map
=================================================================
==2780289==ERROR: AddressSanitizer: use-after-poison on address 0x7e0b87e28d28 at pc 0x55a979a99e7e bp 0x7ffe4b18f0b0 sp 0x7ffe4b18f0a8
READ of size 1 at 0x7e0b87e28d28 thread T0
    #0 0x55a979a99e7d in getStorageClass /usr/local/google/home/thurston/buildbot_repro/llvm-project/llvm/include/llvm/Object/COFF.h:344
    #1 0x55a979a99e7d in isSectionDefinition /usr/local/google/home/thurston/buildbot_repro/llvm-project/llvm/include/llvm/Object/COFF.h:429:9
    #2 0x55a979a99e7d in getSymbols /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/LLDMapFile.cpp:54:42
    #3 0x55a979a99e7d in lld::coff::writeLLDMapFile(lld::coff::COFFLinkerContext const&) /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/LLDMapFile.cpp:103:40
    #4 0x55a979a16879 in (anonymous namespace)::Writer::run() /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/Writer.cpp:810:3
    #5 0x55a979a00aac in lld::coff::writeResult(lld::coff::COFFLinkerContext&) /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/Writer.cpp:354:15
    #6 0x55a97985f7ed in lld::coff::LinkerDriver::linkerMain(llvm::ArrayRef<char const*>) /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/Driver.cpp:2826:3
    #7 0x55a97984cdd3 in lld::coff::link(llvm::ArrayRef<char const*>, llvm::raw_ostream&, llvm::raw_ostream&, bool, bool) /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/Driver.cpp:97:15
    #8 0x55a9797f9793 in lld::unsafeLldMain(llvm::ArrayRef<char const*>, llvm::raw_ostream&, llvm::raw_ostream&, llvm::ArrayRef<lld::DriverDef>, bool) /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/Common/DriverDispatcher.cpp:163:12
    #9 0x55a9797fa3b6 in operator() /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/Common/DriverDispatcher.cpp:188:15
    #10 0x55a9797fa3b6 in void llvm::function_ref<void ()>::callback_fn<lld::lldMain(llvm::ArrayRef<char const*>, llvm::raw_ostream&, llvm::raw_ostream&, llvm::ArrayRef<lld::DriverDef>)::$_0>(long) /usr/local/google/home/thurston/buildbot_repro/llvm-project/llvm/include/llvm/ADT/STLFunctionalExtras.h:46:12
    #11 0x55a97966cb93 in operator() /usr/local/google/home/thurston/buildbot_repro/llvm-project/llvm/include/llvm/ADT/STLFunctionalExtras.h:69:12
    #12 0x55a97966cb93 in llvm::CrashRecoveryContext::RunSafely(llvm::function_ref<void ()>) /usr/local/google/home/thurston/buildbot_repro/llvm-project/llvm/lib/Support/CrashRecoveryContext.cpp:426:3
    #13 0x55a9797f9dc3 in lld::lldMain(llvm::ArrayRef<char const*>, llvm::raw_ostream&, llvm::raw_ostream&, llvm::ArrayRef<lld::DriverDef>) /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/Common/DriverDispatcher.cpp:187:14
    #14 0x55a979627512 in lld_main(int, char**, llvm::ToolContext const&) /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/tools/lld/lld.cpp:103:14
    #15 0x55a979628731 in main /usr/local/google/home/thurston/buildbot_repro/llvm_build_asan/tools/lld/tools/lld/lld-driver.cpp:17:10
    #16 0x7ffb8b202c89 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #17 0x7ffb8b202d44 in __libc_start_main csu/../csu/libc-start.c:360:3
    #18 0x55a97953ef60 in _start (/usr/local/google/home/thurston/buildbot_repro/llvm_build_asan/bin/lld+0x8fd1f60)

0x7e0b87e28d28 is located 40 bytes inside of 4096-byte region [0x7e0b87e28d00,0x7e0b87e29d00)
allocated by thread T0 here:
    #0 0x55a9796259a2 in operator new(unsigned long, std::align_val_t) /usr/local/google/home/thurston/buildbot_repro/llvm-project/compiler-rt/lib/asan/asan_new_delete.cpp:98:3
    #1 0x55a97969d28e in Allocate /usr/local/google/home/thurston/buildbot_repro/llvm-project/llvm/include/llvm/Support/AllocatorBase.h:92:12
    #2 0x55a97969d28e in StartNewSlab /usr/local/google/home/thurston/buildbot_repro/llvm-project/llvm/include/llvm/Support/Allocator.h:346:42
    #3 0x55a97969d28e in llvm::BumpPtrAllocatorImpl<llvm::MallocAllocator, 4096ul, 4096ul, 128ul>::AllocateSlow(unsigned long, unsigned long, llvm::Align) /usr/local/google/home/thurston/buildbot_repro/llvm-project/llvm/include/llvm/Support/Allocator.h:202:5
    #4 0x55a979931f28 in Allocate /usr/local/google/home/thurston/buildbot_repro/llvm-project/llvm/include/llvm/Support/Allocator.h:178:12
    #5 0x55a979931f28 in Allocate /usr/local/google/home/thurston/buildbot_repro/llvm-project/llvm/include/llvm/Support/Allocator.h:216:12
    #6 0x55a979931f28 in Allocate /usr/local/google/home/thurston/buildbot_repro/llvm-project/llvm/include/llvm/Support/AllocatorBase.h:53:43
    #7 0x55a979931f28 in Allocate<llvm::object::coff_symbol_generic> /usr/local/google/home/thurston/buildbot_repro/llvm-project/llvm/include/llvm/Support/AllocatorBase.h:76:29
    #8 0x55a979931f28 in Allocate /usr/local/google/home/thurston/buildbot_repro/llvm-project/llvm/include/llvm/Support/Allocator.h:439:50
    #9 0x55a979931f28 in make<llvm::object::coff_symbol_generic, const llvm::object::coff_symbol_generic &> /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/include/lld/Common/Memory.h:61:46
    #10 0x55a979931f28 in lld::coff::ObjFile::createRegular(llvm::object::COFFSymbolRef) /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/InputFiles.cpp:464:28
    #11 0x55a979934004 in lld::coff::ObjFile::createDefined(llvm::object::COFFSymbolRef, std::__1::vector<llvm::object::coff_aux_section_definition const*, std::__1::allocator<llvm::object::coff_aux_section_definition const*>>&, bool&) /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/InputFiles.cpp:855:10
    #12 0x55a97992a5eb in lld::coff::ObjFile::initializeSymbols() /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/InputFiles.cpp:522:20
    #13 0x55a9799296d5 in lld::coff::ObjFile::parse() /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/InputFiles.cpp:248:3
    #14 0x55a979862534 in lld::coff::LinkerDriver::addFile(lld::coff::InputFile*) /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/Driver.cpp:212:11
    #15 0x55a979866b67 in lld::coff::LinkerDriver::addBuffer(std::__1::unique_ptr<llvm::MemoryBuffer, std::__1::default_delete<llvm::MemoryBuffer>>, bool, bool) /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/Driver.cpp
    #16 0x55a97988b704 in operator() /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/Driver.cpp:358:18
    #17 0x55a97988b704 in __invoke<(lambda at /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/Driver.cpp:323:15) &> /usr/local/google/home/thurston/buildbot_repro/libcxx_build_asan/include/c++/v1/__type_traits/invoke.h:179:25
    #18 0x55a97988b704 in __call<(lambda at /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/Driver.cpp:323:15) &> /usr/local/google/home/thurston/buildbot_repro/libcxx_build_asan/include/c++/v1/__type_traits/invoke.h:251:5
    #19 0x55a97988b704 in __invoke_r<void, (lambda at /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/Driver.cpp:323:15) &> /usr/local/google/home/thurston/buildbot_repro/libcxx_build_asan/include/c++/v1/__type_traits/invoke.h:273:10
    #20 0x55a97988b704 in operator() /usr/local/google/home/thurston/buildbot_repro/libcxx_build_asan/include/c++/v1/__functional/function.h:167:12
    #21 0x55a97988b704 in std::__1::__function::__func<lld::coff::LinkerDriver::enqueuePath(llvm::StringRef, bool, bool)::$_0, std::__1::allocator<lld::coff::LinkerDriver::enqueuePath(llvm::StringRef, bool, bool)::$_0>, void ()>::operator()() /usr/local/google/home/thurston/buildbot_repro/libcxx_build_asan/include/c++/v1/__functional/function.h:319:10
    #22 0x55a979876c06 in operator() /usr/local/google/home/thurston/buildbot_repro/libcxx_build_asan/include/c++/v1/__functional/function.h:436:12
    #23 0x55a979876c06 in operator() /usr/local/google/home/thurston/buildbot_repro/libcxx_build_asan/include/c++/v1/__functional/function.h:995:10
    #24 0x55a979876c06 in lld::coff::LinkerDriver::run() /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/Driver.cpp:1086:5
    #25 0x55a97985ac7e in lld::coff::LinkerDriver::linkerMain(llvm::ArrayRef<char const*>) /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/Driver.cpp:2258:3
    #26 0x55a97984cdd3 in lld::coff::link(llvm::ArrayRef<char const*>, llvm::raw_ostream&, llvm::raw_ostream&, bool, bool) /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/Driver.cpp:97:15
    #27 0x55a9797f9793 in lld::unsafeLldMain(llvm::ArrayRef<char const*>, llvm::raw_ostream&, llvm::raw_ostream&, llvm::ArrayRef<lld::DriverDef>, bool) /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/Common/DriverDispatcher.cpp:163:12
    #28 0x55a9797fa3b6 in operator() /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/Common/DriverDispatcher.cpp:188:15
    #29 0x55a9797fa3b6 in void llvm::function_ref<void ()>::callback_fn<lld::lldMain(llvm::ArrayRef<char const*>, llvm::raw_ostream&, llvm::raw_ostream&, llvm::ArrayRef<lld::DriverDef>)::$_0>(long) /usr/local/google/home/thurston/buildbot_repro/llvm-project/llvm/include/llvm/ADT/STLFunctionalExtras.h:46:12
    #30 0x55a97966cb93 in operator() /usr/local/google/home/thurston/buildbot_repro/llvm-project/llvm/include/llvm/ADT/STLFunctionalExtras.h:69:12
    #31 0x55a97966cb93 in llvm::CrashRecoveryContext::RunSafely(llvm::function_ref<void ()>) /usr/local/google/home/thurston/buildbot_repro/llvm-project/llvm/lib/Support/CrashRecoveryContext.cpp:426:3
    #32 0x55a9797f9dc3 in lld::lldMain(llvm::ArrayRef<char const*>, llvm::raw_ostream&, llvm::raw_ostream&, llvm::ArrayRef<lld::DriverDef>) /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/Common/DriverDispatcher.cpp:187:14
    #33 0x55a979627512 in lld_main(int, char**, llvm::ToolContext const&) /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/tools/lld/lld.cpp:103:14
    #34 0x55a979628731 in main /usr/local/google/home/thurston/buildbot_repro/llvm_build_asan/tools/lld/tools/lld/lld-driver.cpp:17:10
    #35 0x7ffb8b202c89 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16

SUMMARY: AddressSanitizer: use-after-poison /usr/local/google/home/thurston/buildbot_repro/llvm-project/llvm/include/llvm/Object/COFF.h:344 in getStorageClass
Shadow bytes around the buggy address:
  0x7e0b87e28a80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7e0b87e28b00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7e0b87e28b80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7e0b87e28c00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7e0b87e28c80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x7e0b87e28d00: 00 00 00 00 04[f7]f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
  0x7e0b87e28d80: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
  0x7e0b87e28e00: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
  0x7e0b87e28e80: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
  0x7e0b87e28f00: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
  0x7e0b87e28f80: f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==2780289==ABORTING

@dyung
Copy link
Collaborator

dyung commented Jan 22, 2025

We are also seeing the same test failure on our internal ASAN builder on Windows.

thurstond added a commit that referenced this pull request Jan 22, 2025
#123877)

Reverts #122811 due to buildbot breakage e.g.,
https://lab.llvm.org/buildbot/#/builders/52/builds/5421/steps/11/logs/stdio

ASan output from local re-run:
```
==2780289==ERROR: AddressSanitizer: use-after-poison on address 0x7e0b87e28d28 at pc 0x55a979a99e7e bp 0x7ffe4b18f0b0 sp 0x7ffe4b18f0a8
READ of size 1 at 0x7e0b87e28d28 thread T0
    #0 0x55a979a99e7d in getStorageClass /usr/local/google/home/thurston/buildbot_repro/llvm-project/llvm/include/llvm/Object/COFF.h:344
    #1 0x55a979a99e7d in isSectionDefinition /usr/local/google/home/thurston/buildbot_repro/llvm-project/llvm/include/llvm/Object/COFF.h:429:9
    #2 0x55a979a99e7d in getSymbols /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/LLDMapFile.cpp:54:42
    #3 0x55a979a99e7d in lld::coff::writeLLDMapFile(lld::coff::COFFLinkerContext const&) /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/LLDMapFile.cpp:103:40
    #4 0x55a979a16879 in (anonymous namespace)::Writer::run() /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/Writer.cpp:810:3
    #5 0x55a979a00aac in lld::coff::writeResult(lld::coff::COFFLinkerContext&) /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/Writer.cpp:354:15
    #6 0x55a97985f7ed in lld::coff::LinkerDriver::linkerMain(llvm::ArrayRef<char const*>) /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/Driver.cpp:2826:3
    #7 0x55a97984cdd3 in lld::coff::link(llvm::ArrayRef<char const*>, llvm::raw_ostream&, llvm::raw_ostream&, bool, bool) /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/Driver.cpp:97:15
    #8 0x55a9797f9793 in lld::unsafeLldMain(llvm::ArrayRef<char const*>, llvm::raw_ostream&, llvm::raw_ostream&, llvm::ArrayRef<lld::DriverDef>, bool) /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/Common/DriverDispatcher.cpp:163:12
    #9 0x55a9797fa3b6 in operator() /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/Common/DriverDispatcher.cpp:188:15
    #10 0x55a9797fa3b6 in void llvm::function_ref<void ()>::callback_fn<lld::lldMain(llvm::ArrayRef<char const*>, llvm::raw_ostream&, llvm::raw_ostream&, llvm::ArrayRef<lld::DriverDef>)::$_0>(long) /usr/local/google/home/thurston/buildbot_repro/llvm-project/llvm/include/llvm/ADT/STLFunctionalExtras.h:46:12
    #11 0x55a97966cb93 in operator() /usr/local/google/home/thurston/buildbot_repro/llvm-project/llvm/include/llvm/ADT/STLFunctionalExtras.h:69:12
    #12 0x55a97966cb93 in llvm::CrashRecoveryContext::RunSafely(llvm::function_ref<void ()>) /usr/local/google/home/thurston/buildbot_repro/llvm-project/llvm/lib/Support/CrashRecoveryContext.cpp:426:3
    #13 0x55a9797f9dc3 in lld::lldMain(llvm::ArrayRef<char const*>, llvm::raw_ostream&, llvm::raw_ostream&, llvm::ArrayRef<lld::DriverDef>) /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/Common/DriverDispatcher.cpp:187:14
    #14 0x55a979627512 in lld_main(int, char**, llvm::ToolContext const&) /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/tools/lld/lld.cpp:103:14
    #15 0x55a979628731 in main /usr/local/google/home/thurston/buildbot_repro/llvm_build_asan/tools/lld/tools/lld/lld-driver.cpp:17:10
    #16 0x7ffb8b202c89 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #17 0x7ffb8b202d44 in __libc_start_main csu/../csu/libc-start.c:360:3
    #18 0x55a97953ef60 in _start (/usr/local/google/home/thurston/buildbot_repro/llvm_build_asan/bin/lld+0x8fd1f60)
```
@thurstond
Copy link
Contributor

I've reverted this patch with #123877 to (hopefully) bring the buildbots back to green.

(I confirmed that the ASan buildbot passed with LLVM source at the immediately preceding commit [c59ede6].)

github-actions bot pushed a commit to arm/arm-toolchain that referenced this pull request Jan 22, 2025
…der objects" (#123877)

Reverts llvm/llvm-project#122811 due to buildbot breakage e.g.,
https://lab.llvm.org/buildbot/#/builders/52/builds/5421/steps/11/logs/stdio

ASan output from local re-run:
```
==2780289==ERROR: AddressSanitizer: use-after-poison on address 0x7e0b87e28d28 at pc 0x55a979a99e7e bp 0x7ffe4b18f0b0 sp 0x7ffe4b18f0a8
READ of size 1 at 0x7e0b87e28d28 thread T0
    #0 0x55a979a99e7d in getStorageClass /usr/local/google/home/thurston/buildbot_repro/llvm-project/llvm/include/llvm/Object/COFF.h:344
    #1 0x55a979a99e7d in isSectionDefinition /usr/local/google/home/thurston/buildbot_repro/llvm-project/llvm/include/llvm/Object/COFF.h:429:9
    #2 0x55a979a99e7d in getSymbols /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/LLDMapFile.cpp:54:42
    #3 0x55a979a99e7d in lld::coff::writeLLDMapFile(lld::coff::COFFLinkerContext const&) /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/LLDMapFile.cpp:103:40
    #4 0x55a979a16879 in (anonymous namespace)::Writer::run() /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/Writer.cpp:810:3
    #5 0x55a979a00aac in lld::coff::writeResult(lld::coff::COFFLinkerContext&) /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/Writer.cpp:354:15
    #6 0x55a97985f7ed in lld::coff::LinkerDriver::linkerMain(llvm::ArrayRef<char const*>) /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/Driver.cpp:2826:3
    #7 0x55a97984cdd3 in lld::coff::link(llvm::ArrayRef<char const*>, llvm::raw_ostream&, llvm::raw_ostream&, bool, bool) /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/COFF/Driver.cpp:97:15
    #8 0x55a9797f9793 in lld::unsafeLldMain(llvm::ArrayRef<char const*>, llvm::raw_ostream&, llvm::raw_ostream&, llvm::ArrayRef<lld::DriverDef>, bool) /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/Common/DriverDispatcher.cpp:163:12
    #9 0x55a9797fa3b6 in operator() /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/Common/DriverDispatcher.cpp:188:15
    #10 0x55a9797fa3b6 in void llvm::function_ref<void ()>::callback_fn<lld::lldMain(llvm::ArrayRef<char const*>, llvm::raw_ostream&, llvm::raw_ostream&, llvm::ArrayRef<lld::DriverDef>)::$_0>(long) /usr/local/google/home/thurston/buildbot_repro/llvm-project/llvm/include/llvm/ADT/STLFunctionalExtras.h:46:12
    #11 0x55a97966cb93 in operator() /usr/local/google/home/thurston/buildbot_repro/llvm-project/llvm/include/llvm/ADT/STLFunctionalExtras.h:69:12
    #12 0x55a97966cb93 in llvm::CrashRecoveryContext::RunSafely(llvm::function_ref<void ()>) /usr/local/google/home/thurston/buildbot_repro/llvm-project/llvm/lib/Support/CrashRecoveryContext.cpp:426:3
    #13 0x55a9797f9dc3 in lld::lldMain(llvm::ArrayRef<char const*>, llvm::raw_ostream&, llvm::raw_ostream&, llvm::ArrayRef<lld::DriverDef>) /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/Common/DriverDispatcher.cpp:187:14
    #14 0x55a979627512 in lld_main(int, char**, llvm::ToolContext const&) /usr/local/google/home/thurston/buildbot_repro/llvm-project/lld/tools/lld/lld.cpp:103:14
    #15 0x55a979628731 in main /usr/local/google/home/thurston/buildbot_repro/llvm_build_asan/tools/lld/tools/lld/lld-driver.cpp:17:10
    #16 0x7ffb8b202c89 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #17 0x7ffb8b202d44 in __libc_start_main csu/../csu/libc-start.c:360:3
    #18 0x55a97953ef60 in _start (/usr/local/google/home/thurston/buildbot_repro/llvm_build_asan/bin/lld+0x8fd1f60)
```
@mstorsjo
Copy link
Member Author

Sorry about this, and thanks for doing the revert! I'll look into the issue.

mstorsjo added a commit to mstorsjo/llvm-project that referenced this pull request Jan 22, 2025
ecb5ea6 tried to fix cases when LLD
links what seems to be import library header objects from MSVC. However,
the fix seems incorrect; the review at https://reviews.llvm.org/D133627
concluded that if this (treating this kind of symbol as a common symbol)
is what link.exe does, it's fine.

However, this is most probably not what link.exe does. The symbol
mentioned in the commit message of
ecb5ea6 would be a common symbol with a
size of around 3 GB; this is not what might have been intended.

That commit tried to avoid running into the error ".idata$4 should not
refer to special section 0"; that issue is fixed for a similar style of
section symbols in 4a4a8a1.

Therefore, revert ecb5ea6 and extend
the fix from 4a4a8a1 to also work for
the section symbols in MSVC generated import libraries.

The main detail about them, is that for symbols of type
IMAGE_SYM_CLASS_SECTION, the Value field is not an offset, but it is an
optional set of flags, corresponding to the Characteristics of the
section header (although it may be empty).

This is a reland of a previous version of this commit, earlier
merged in 9457418 / llvm#122811. The
previous version failed tests when run with address sanitizer.
The issue was that the synthesized coff_symbol_generic object
actually will be used to access a full coff_symbol16 or coff_symbol32
struct, see DefinedCOFF::getCOFFSymbol. Therefore, we need to
make a copy of the full size of either of them.
mstorsjo added a commit to mstorsjo/llvm-project that referenced this pull request Jan 22, 2025
ecb5ea6 tried to fix cases when LLD
links what seems to be import library header objects from MSVC. However,
the fix seems incorrect; the review at https://reviews.llvm.org/D133627
concluded that if this (treating this kind of symbol as a common symbol)
is what link.exe does, it's fine.

However, this is most probably not what link.exe does. The symbol
mentioned in the commit message of
ecb5ea6 would be a common symbol with a
size of around 3 GB; this is not what might have been intended.

That commit tried to avoid running into the error ".idata$4 should not
refer to special section 0"; that issue is fixed for a similar style of
section symbols in 4a4a8a1.

Therefore, revert ecb5ea6 and extend
the fix from 4a4a8a1 to also work for
the section symbols in MSVC generated import libraries.

The main detail about them, is that for symbols of type
IMAGE_SYM_CLASS_SECTION, the Value field is not an offset, but it is an
optional set of flags, corresponding to the Characteristics of the
section header (although it may be empty).

This is a reland of a previous version of this commit, earlier
merged in 9457418 / llvm#122811. The
previous version failed tests when run with address sanitizer.
The issue was that the synthesized coff_symbol_generic object
actually will be used to access a full coff_symbol16 or coff_symbol32
struct, see DefinedCOFF::getCOFFSymbol. Therefore, we need to
make a copy of the full size of either of them.
mstorsjo added a commit that referenced this pull request Jan 23, 2025
…123916)

ecb5ea6 tried to fix cases when LLD
links what seems to be import library header objects from MSVC. However,
the fix seems incorrect; the review at https://reviews.llvm.org/D133627
concluded that if this (treating this kind of symbol as a common symbol)
is what link.exe does, it's fine.

However, this is most probably not what link.exe does. The symbol
mentioned in the commit message of
ecb5ea6 would be a common symbol with a
size of around 3 GB; this is not what might have been intended.

That commit tried to avoid running into the error ".idata$4 should not
refer to special section 0"; that issue is fixed for a similar style of
section symbols in 4a4a8a1.

Therefore, revert ecb5ea6 and extend
the fix from 4a4a8a1 to also work for
the section symbols in MSVC generated import libraries.

The main detail about them, is that for symbols of type
IMAGE_SYM_CLASS_SECTION, the Value field is not an offset, but it is an
optional set of flags, corresponding to the Characteristics of the
section header (although it may be empty).

This is a reland of a previous version of this commit, earlier merged in
9457418 / #122811. The previous version
failed tests when run with address sanitizer. The issue was that the
synthesized coff_symbol_generic object actually will be used to access a
full coff_symbol16 or coff_symbol32 struct, see
DefinedCOFF::getCOFFSymbol. Therefore, we need to make a copy of the
full size of either of them.
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.

6 participants