Skip to content

Commit 2f5b3d9

Browse files
committed
[reproduce] Preserve linker inputs when reproduce link fails
Capture linker scripts and unresolved namespecs when input activation fails so the reproduce response preserves the original failure. Avoid applying mapping-file resolution to unmapped replay namespecs while retaining normal mapped namespec behavior. Fix: #1014 Signed-off-by: Steven Ramirez Rosa <ramirezr@qti.qualcomm.com>
1 parent faeb0be commit 2f5b3d9

4 files changed

Lines changed: 83 additions & 6 deletions

File tree

‎include/eld/Input/Input.h‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@ class Input {
142142
// -----------------------Namespec support -------------------------------
143143
bool resolvePath(const LinkerConfig &PConfig);
144144

145+
/// Resolve an input using reproduce mapping semantics.
146+
/// Unmapped namespecs retain normal -l search behavior.
147+
bool resolveReproducePath(const LinkerConfig &PConfig);
148+
145149
bool resolvePathMappingFile(const LinkerConfig &PConfig);
146150

147151
/// -------------------------Input Type ---------------------------

‎lib/Input/Input.cpp‎

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,19 @@ bool Input::resolvePathMappingFile(const LinkerConfig &PConfig) {
7777
return true;
7878
}
7979

80+
bool Input::resolveReproducePath(const LinkerConfig &PConfig) {
81+
if (!PConfig.options().hasMappingFile() || isInternal())
82+
return false;
83+
84+
// An unmapped namespec must retain normal -l search semantics. A namespec
85+
// with an explicit mapping entry still uses mapping-file behavior so normal
86+
// mapped links are unchanged.
87+
if (Type == Input::Namespec && !PConfig.hasMappingForFile(FileName))
88+
return false;
89+
90+
return resolvePathMappingFile(PConfig);
91+
}
92+
8093
bool Input::isPathValid(const std::string &Path) const {
8194
if (llvm::sys::fs::is_directory(Path)) {
8295
DiagEngine->raise(Diag::fatal_cannot_read_input_err)
@@ -109,8 +122,8 @@ std::string Input::expandSysrootMarkers(llvm::StringRef Name,
109122
bool Input::resolvePath(const LinkerConfig &PConfig) {
110123
if (ResolvedPath)
111124
return true;
112-
if (PConfig.options().hasMappingFile() && !isInternal())
113-
return resolvePathMappingFile(PConfig);
125+
if (resolveReproducePath(PConfig))
126+
return true;
114127
// Apply --remap-inputs remappings (in order, first match wins).
115128
if (auto Replacement = PConfig.options().findRemapInput(FileName)) {
116129
if (PConfig.getPrinter()->isVerbose())

‎lib/LinkerWrapper/GnuLdDriver.cpp‎

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1746,13 +1746,20 @@ bool GnuLdDriver::processReproduceOption(
17461746
break;
17471747
case T::l:
17481748
case T::namespec: {
1749+
bool foundAction = false;
17491750
for (size_t i = lastNamespecId + 1; i < actions.size(); ++i) {
17501751
auto action = actions[i];
17511752
if (action->getInputActionKind() == eld::InputAction::Namespec) {
1753+
foundAction = true;
17521754
lastNamespecId = i;
17531755
auto ipt = action->getInput();
1754-
if (!ipt)
1755-
return false;
1756+
// Input activation may have failed before this action was fully
1757+
// resolved. Preserve the original namespec so replay reports the
1758+
// same failure instead of silently dropping the input.
1759+
if (!ipt || !ipt->getInputFile()) {
1760+
os << arg->getSpelling() << arg->getValue() << ' ';
1761+
break;
1762+
}
17561763
// Use the mapped path as the rewrite key. For a namespec input
17571764
// e.g. "-l2", use "2" instead of "lib2.so". This will match the
17581765
// form in the mapping file "2=SharedLibrary/2.<hash>".
@@ -1763,6 +1770,8 @@ bool GnuLdDriver::processReproduceOption(
17631770
break;
17641771
}
17651772
}
1773+
if (!foundAction)
1774+
os << arg->getSpelling() << arg->getValue() << ' ';
17661775
break;
17671776
}
17681777
case T::INPUT: {
@@ -1796,10 +1805,34 @@ bool GnuLdDriver::processReproduceOption(
17961805
<< getRewrittenRemappedPath(arg->getValue()) << ' ';
17971806
break;
17981807
case T::T: {
1808+
// Capture the script before looking for its activated action. Input
1809+
// activation can stop at an earlier failing input, leaving this action
1810+
// without an Input even though the script itself is readable.
1811+
std::string ScriptPath = Input::expandSysrootMarkers(
1812+
arg->getValue(), Config.directories(), *Config.getDiagEngine());
1813+
std::string ResolvedPath;
1814+
if (llvm::sys::fs::exists(ScriptPath))
1815+
ResolvedPath = ScriptPath;
1816+
else if (const eld::sys::fs::Path *Resolved = Config.directories().find(
1817+
arg->getValue(), eld::SearchDirs::SearchInputType::Script))
1818+
ResolvedPath = Resolved->native();
1819+
if (!ResolvedPath.empty())
1820+
outputTar->createAndAddScriptFile(arg->getValue(), ResolvedPath);
1821+
17991822
auto path = getRewrittenActionInputPath(eld::InputAction::Script,
18001823
lastScriptId, arg->getValue());
1801-
if (!path)
1802-
return false;
1824+
if (!path) {
1825+
// A preceding input can fail before this script action is activated.
1826+
// Capture a script that can still be resolved so replay does not
1827+
// replace the original input error with a missing-script error.
1828+
if (!ResolvedPath.empty()) {
1829+
os << arg->getSpelling() << ' '
1830+
<< outputTar->rewritePath(arg->getValue()) << ' ';
1831+
} else {
1832+
os << arg->getAsString(Args) << ' ';
1833+
}
1834+
break;
1835+
}
18031836
os << arg->getSpelling() << ' ' << *path << ' ';
18041837
break;
18051838
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#---ReplayFailedInput.test----------------------------- Executable ------------#
2+
#BEGIN_COMMENT
3+
# Verify that --reproduce captures a linker script and preserves an unresolved
4+
# namespec when an earlier input prevents normal input processing from reaching
5+
# the script.
6+
#END_COMMENT
7+
#START_TEST
8+
RUN: %rm %t.A %t.repro.tar %t.repro
9+
RUN: %mkdir %t.A
10+
RUN: %echo "INPUT(/lib1.so)" > %t.A/script.t
11+
RUN: %clang %clangopts -c %p/Inputs/1.c -o %t.1.o
12+
RUN: %clang %clangopts -c %p/Inputs/2.c -o %t.2.o
13+
RUN: %link %linkopts --no-default-plugins -shared -soname foo %t.1.o -o %t.A/lib1.so
14+
RUN: %not %link %linkopts --no-default-plugins %t.2.o -l1 -T %t.A/script.t \
15+
RUN: -o %t.out --sysroot %t.A --reproduce %t.repro.tar 2>&1 | %filecheck %s --check-prefix=ORIGINAL
16+
RUN: %mkdir %t.repro
17+
RUN: cd %t.repro && %tar -xf %t.repro.tar --strip-components=1
18+
RUN: %filecheck %s --check-prefix=MAPPING < %t.repro/mapping.ini
19+
RUN: %filecheck %s --check-prefix=RESPONSE < %t.repro/response.txt
20+
RUN: cd %t.repro && %not bash response.txt 2>&1 | %filecheck %s --check-prefix=REPLAY
21+
22+
ORIGINAL: Fatal: cannot recognize namespec -l1
23+
MAPPING: [Linker Scripts]
24+
MAPPING-NEXT: {{.*}}script.t=LinkerScript/
25+
RESPONSE: -l1
26+
REPLAY: Fatal: cannot recognize namespec -l1
27+
#END_TEST

0 commit comments

Comments
 (0)