llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
29.12k stars 12.01k forks source link

[LLD][COFF] `start-lib` / `end-lib` incorrectly discards symbols referenced in the same library #109707

Open yuzhy8701 opened 1 month ago

yuzhy8701 commented 1 month ago

When linking in a library with start-lib and end-lib, lld-link would discard a symbol unless it is already seen in the prior objects. It discards the symbol even if the symbol is referenced in a latter object from the same library (latter as the command line order).

A simple case to repro (Powershell on Windows):

  1. Setup

    Set-Content -Path .\hello-time.h -Value @"
    #ifndef LIB_HELLO_TIME_H_
    #define LIB_HELLO_TIME_H_
    
    #include <string>
    
    std::string get_localtime();
    
    #endif
    "@
    
    Set-Content -Path .\hello-time.cc -Value @"
    #include "hello-time.h"
    #include <ctime>
    #include <string>
    
    std::string get_localtime() {
      std::time_t result = std::time(nullptr);
      return std::asctime(std::localtime(&result));
    }
    "@
    
    Set-Content -Path .\hello-greet.h -Value @"
    #ifndef MAIN_HELLO_GREET_H_
    #define MAIN_HELLO_GREET_H_
    
    #include <string>
    
    std::string get_greet(const std::string &thing);
    
    #endif
    "@
    
    Set-Content -Path .\hello-greet.cc -Value @"
    #include "hello-time.h"
    #include "hello-greet.h"
    #include <string>
    
    std::string get_greet(const std::string& who) {
      return "Hello " + who + "; " + get_localtime();
    }
    "@
    
    Set-Content -Path .\hello-world.cc -Value @"
    #include "hello-greet.h"
    #include <iostream>
    #include <string>
    
    int main(int argc, char** argv) {
      std::cout << get_greet("world") << std::endl;
      return 0;
    }
    "@
  2. The following succeeds:

    If (Test-Path 'out') {
        Remove-Item -Path 'out' -Recurse -Force -Verbose
    }
    New-Item -Path 'out' -ItemType Directory
    
    clang-cl.exe /c 'hello-time.cc' /Fo'out/hello-time.obj'
    clang-cl.exe /c 'hello-greet.cc' /Fo'out/hello-greet.obj'
    clang-cl.exe /c 'hello-world.cc' /Fo'out/hello-world.obj'
    
    lld-link.exe /OUT:'out/hello-world.exe' `
        out/hello-world.obj `
        '/start-lib' `
        out/hello-greet.obj `
        out/hello-time.obj `
        '/end-lib'
  3. The following fails with error: lld-link: error: relocation against symbol in discarded section: ?get_localtime@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ

    $ErrorActionPreference = "Stop"
    
    If (Test-Path 'out') {
        Remove-Item -Path 'out' -Recurse -Force -Verbose
    }
    New-Item -Path 'out' -ItemType Directory
    
    clang-cl.exe /c 'hello-time.cc' /Fo'out/hello-time.obj'
    clang-cl.exe /c 'hello-greet.cc' /Fo'out/hello-greet.obj'
    clang-cl.exe /c 'hello-world.cc' /Fo'out/hello-world.obj'
    
    lld-link.exe /OUT:'out/hello-world.exe' `
        out/hello-world.obj `
        '/start-lib' `
        out/hello-time.obj `
        out/hello-greet.obj `
        '/end-lib'

Note that the only difference between success and fail cases is the object order between start-lib and end-lib. I'm able to reproduce with the latest llvm version (19.1.0).

llvmbot commented 1 month ago

@llvm/issue-subscribers-lld-coff

Author: None (yuzhy8701)

When linking in a library with `start-lib` and `end-lib`, `lld-link` would discard a symbol unless it is already seen in the prior objects. It discards the symbol even if the symbol is referenced in a latter object from the same library (latter as the command line order). ## A simple case to repro (Powershell on Windows): 1. Setup ```powershell Set-Content -Path .\hello-time.h -Value @" #ifndef LIB_HELLO_TIME_H_ #define LIB_HELLO_TIME_H_ #include <string> std::string get_localtime(); #endif "@ Set-Content -Path .\hello-time.cc -Value @" #include "hello-time.h" #include <ctime> #include <string> std::string get_localtime() { std::time_t result = std::time(nullptr); return std::asctime(std::localtime(&result)); } "@ Set-Content -Path .\hello-greet.h -Value @" #ifndef MAIN_HELLO_GREET_H_ #define MAIN_HELLO_GREET_H_ #include <string> std::string get_greet(const std::string &thing); #endif "@ Set-Content -Path .\hello-greet.cc -Value @" #include "hello-time.h" #include "hello-greet.h" #include <string> std::string get_greet(const std::string& who) { return "Hello " + who + "; " + get_localtime(); } "@ Set-Content -Path .\hello-world.cc -Value @" #include "hello-greet.h" #include <iostream> #include <string> int main(int argc, char** argv) { std::cout << get_greet("world") << std::endl; return 0; } "@ ``` 2. The following succeeds: ```powershell If (Test-Path 'out') { Remove-Item -Path 'out' -Recurse -Force -Verbose } New-Item -Path 'out' -ItemType Directory clang-cl.exe /c 'hello-time.cc' /Fo'out/hello-time.obj' clang-cl.exe /c 'hello-greet.cc' /Fo'out/hello-greet.obj' clang-cl.exe /c 'hello-world.cc' /Fo'out/hello-world.obj' lld-link.exe /OUT:'out/hello-world.exe' ` out/hello-world.obj ` '/start-lib' ` out/hello-greet.obj ` out/hello-time.obj ` '/end-lib' ``` 3. The following fails with error: `lld-link: error: relocation against symbol in discarded section: ?get_localtime@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ` ```powershell $ErrorActionPreference = "Stop" If (Test-Path 'out') { Remove-Item -Path 'out' -Recurse -Force -Verbose } New-Item -Path 'out' -ItemType Directory clang-cl.exe /c 'hello-time.cc' /Fo'out/hello-time.obj' clang-cl.exe /c 'hello-greet.cc' /Fo'out/hello-greet.obj' clang-cl.exe /c 'hello-world.cc' /Fo'out/hello-world.obj' lld-link.exe /OUT:'out/hello-world.exe' ` out/hello-world.obj ` '/start-lib' ` out/hello-time.obj ` out/hello-greet.obj ` '/end-lib' ``` **Note that the only difference between success and fail cases is the object order between `start-lib` and `end-lib`.** I'm able to reproduce with the latest llvm version (19.1.0).