Skip to content

Commit 4d4ad88

Browse files
committed
linkerscript: fix ENTRY() last-wins and honour -e
EntryCmd::activate() guarded with hasEntry(), making the first ENTRY() win over later ones and allowing script ENTRY() to overwrite a -e value set on the command line. Similar to GNU ld, replace the guard with !isEntryFromCmdLine() so each ENTRY() overwrites the previous (last wins), while -e is always preserved regardless of script processing order. Add EntryCmdPrecedence.test covering: last-wins within one script, -e override, last -e wins, raw-address -e 0, cross-script last-wins, and -e placed before -T. Resolves #1538
1 parent a30687e commit 4d4ad88

9 files changed

Lines changed: 81 additions & 2 deletions

File tree

include/eld/Config/GeneralOptions.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,10 @@ class GeneralOptions {
754754

755755
bool hasEntry() const;
756756

757+
void setEntryFromCmdLine();
758+
759+
bool isEntryFromCmdLine() const;
760+
757761
llvm::ArrayRef<std::string> mapStyle() const { return MapStyles; }
758762

759763
bool setMapStyle(llvm::StringRef MapStyle);
@@ -1396,6 +1400,7 @@ class GeneralOptions {
13961400
std::vector<std::string> LTOOutputFile;
13971401
std::optional<uint64_t> ImageBase; // --image-base=value
13981402
std::string Entry;
1403+
bool EntryFromCmdLine = false;
13991404
SymbolRenameMap SymbolRenames;
14001405
AddressMapType AddressMap;
14011406
std::vector<const char *> CommandLineArgs;

lib/Config/GeneralOptions.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ void GeneralOptions::setEntry(const std::string &PEntry) { Entry = PEntry; }
127127

128128
bool GeneralOptions::hasEntry() const { return !Entry.empty(); }
129129

130+
bool GeneralOptions::isEntryFromCmdLine() const { return EntryFromCmdLine; }
131+
132+
void GeneralOptions::setEntryFromCmdLine() { EntryFromCmdLine = true; }
133+
130134
void GeneralOptions::setTrace(bool EnableTrace) {
131135
DiagEngine->getPrinter()->setTrace(DiagEngine->getPrinter()->TraceFiles);
132136
}

lib/LinkerWrapper/GnuLdDriver.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,7 @@ bool GnuLdDriver::processOptions(llvm::opt::InputArgList &Args) {
713713
// set up entry point from -e
714714
if (llvm::opt::Arg *arg = Args.getLastArg(T::entrypoint)) {
715715
Config.options().setEntry(arg->getValue());
716+
Config.options().setEntryFromCmdLine();
716717
Config.addCommandLine(Table->getOptionName(T::entrypoint), arg->getValue());
717718
}
718719

lib/Script/EntryCmd.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ void EntryCmd::dump(llvm::raw_ostream &Outs) const {
3232
}
3333

3434
eld::Expected<void> EntryCmd::activate(Module &CurModule) {
35-
if ((!EntrySymbol.empty()) && (!(CurModule.getConfig().options().hasEntry())))
36-
CurModule.getConfig().options().setEntry(EntrySymbol);
35+
GeneralOptions &Options = CurModule.getConfig().options();
36+
if (!EntrySymbol.empty() && !Options.isEntryFromCmdLine())
37+
Options.setEntry(EntrySymbol);
3738
return eld::Expected<void>();
3839
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#---EntryCmdPrecedence.test-------------- Executable,LS ----------------------#
2+
#BEGIN_COMMENT
3+
# Tests GNU ld entry point precedence rules:
4+
# Case 1: Last ENTRY() in a single script wins over earlier ENTRY() commands.
5+
# Case 2: Command-line -e always overrides any script ENTRY().
6+
# Case 3: Last -e on command line wins over earlier -e.
7+
# Case 4: -e with a raw address overrides script ENTRY().
8+
# Case 5: Across multiple -T scripts, the last ENTRY() activated wins.
9+
# Case 6: -e overrides script ENTRY() regardless of command-line order
10+
# (i.e. -e placed before -T still wins).
11+
#
12+
# Input scripts fix symbol addresses so entry points can be verified with
13+
# literal address checks (same style as EntryCmd.test):
14+
# two_entries.t : bar=0x1000, foo=0x2000
15+
# script.t : foo=0x1000, bar=0x2000
16+
# entry_foo.t : bar=0x1000, foo=0x2000 (SECTIONS; entry_bar.t is ENTRY only)
17+
#END_COMMENT
18+
#START_TEST
19+
RUN: %clang %clangopts -c %p/Inputs/1.c -o %t1.o -ffunction-sections
20+
21+
# Case 1: ENTRY(bar) then ENTRY(foo) in one script -- foo must win (last wins).
22+
RUN: %link %linkopts %t1.o -T %p/Inputs/two_entries.t -o %t.lastwins.out
23+
RUN: %readelf -h %t.lastwins.out | %filecheck %s --check-prefix=LASTWINS
24+
#LASTWINS: Entry point address: 0x2000
25+
26+
# Case 2: -e bar overrides the script ENTRY(foo) -- command line always wins.
27+
RUN: %link %linkopts %t1.o -T %p/Inputs/two_entries.t -e bar -o %t.cmdline.out
28+
RUN: %readelf -h %t.cmdline.out | %filecheck %s --check-prefix=CMDLINE
29+
#CMDLINE: Entry point address: 0x1000
30+
31+
# Case 3: -e foo -e bar -- bar must win (last -e wins).
32+
RUN: %link %linkopts %t1.o -T %p/Inputs/script.t -e foo -e bar -o %t.lastcmd.out
33+
RUN: %readelf -h %t.lastcmd.out | %filecheck %s --check-prefix=LASTCMD
34+
#LASTCMD: Entry point address: 0x2000
35+
36+
# Case 4: -e 0 overrides script ENTRY(foo) -- entry must be 0x0.
37+
RUN: %link %linkopts %t1.o -T %p/Inputs/script.t -e 0 -o %t.rawaddr.out
38+
RUN: %readelf -h %t.rawaddr.out | %filecheck %s --check-prefix=RAWADDR
39+
#RAWADDR: Entry point address: 0x0
40+
41+
# Case 5: Two separate scripts, ENTRY(bar) then ENTRY(foo) -- last script wins.
42+
RUN: %link %linkopts %t1.o -T %p/Inputs/entry_bar.t -T %p/Inputs/entry_foo.t -o %t.crossscript.out
43+
RUN: %readelf -h %t.crossscript.out | %filecheck %s --check-prefix=CROSSSCRIPT
44+
#CROSSSCRIPT: Entry point address: 0x2000
45+
46+
# Case 6: -e bar placed BEFORE -T -- command line still wins over script ENTRY(foo).
47+
RUN: %link %linkopts %t1.o -e bar -T %p/Inputs/two_entries.t -o %t.eorder.out
48+
RUN: %readelf -h %t.eorder.out | %filecheck %s --check-prefix=EORDER
49+
#EORDER: Entry point address: 0x1000
50+
#END_TEST
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ENTRY(bar)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
ENTRY(foo)
2+
SECTIONS {
3+
. = 0x1000;
4+
.bar : { *(.text.bar) }
5+
. = 0x2000;
6+
.foo : { *(.text.foo) }
7+
}

test/Common/standalone/linkerscript/EntryCmd/Inputs/script.t

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@ ENTRY(foo)
22
SECTIONS {
33
. = 0x1000;
44
.foo : { *(.text.foo) }
5+
. = 0x2000;
6+
.bar : { *(.text.bar) }
57
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
ENTRY(bar)
2+
ENTRY(foo)
3+
SECTIONS {
4+
. = 0x1000;
5+
.bar : { *(.text.bar) }
6+
. = 0x2000;
7+
.foo : { *(.text.foo) }
8+
}

0 commit comments

Comments
 (0)