Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/userguide/documentation/diagnostic_reports.rst
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ General Warning Flags
* - ``-Werror``
- Treats all warnings as errors, halting the link process.
* - ``-Wlinker-script``
- Enables warnings specific to linker script issues, such as malformed directives, deprecated syntax, or unsupported constructs.
- Enables warnings specific to linker script issues, such as malformed directives, deprecated syntax, unsupported constructs or specifying multiple entry points.
* - ``-Wlinker-script-memory``
- Focuses on memory region definitions in linker scripts, such as overlaps or undefined regions.
* - ``-Wwhole-archive``
Expand Down
1 change: 1 addition & 0 deletions docs/userguide/documentation/linker_faq.rst
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@ Multiple ways to provide entry point to linker
* Initialising the value of linker symbol "start"
* Specifying the start address for the first input section in linker script
(eg: .text : AT(0))
* When multiple -e options are given, the last one takes effect.

How to obtain a non-executable stack
--------------------------------------
Expand Down
2 changes: 2 additions & 0 deletions docs/userguide/documentation/linker_script.rst
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,8 @@ Syntax :- ``ENTRY(symbol)``
point.
- The entry point is the first instruction that is executed after a
program is loaded.
- When multiple ENTRY() commands appear (in one script or across
several -T scripts), the last one takes effect.
- This command is equivalent to the linker command-line option
:option:`-e`.

Expand Down
5 changes: 5 additions & 0 deletions include/eld/Config/GeneralOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,10 @@ class GeneralOptions {

bool hasEntry() const;

void setEntryFromCmdLine();

bool isEntryFromCmdLine() const;

Comment thread
parth-07 marked this conversation as resolved.
llvm::ArrayRef<std::string> mapStyle() const { return MapStyles; }

bool setMapStyle(llvm::StringRef MapStyle);
Expand Down Expand Up @@ -1395,6 +1399,7 @@ class GeneralOptions {
std::vector<std::string> LTOOutputFile;
std::optional<uint64_t> ImageBase; // --image-base=value
std::string Entry;
bool EntryFromCmdLine = false;
SymbolRenameMap SymbolRenames;
AddressMapType AddressMap;
std::vector<const char *> CommandLineArgs;
Expand Down
2 changes: 2 additions & 0 deletions include/eld/Diagnostics/DiagLDScript.inc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ DIAG(assert_failed, DiagnosticEngine::Error, "Assertion failed %0")
DIAG(error_printcmd, DiagnosticEngine::Error, "%0: PRINT: %1")
DIAG(linker_script_uses_phdrs_no_sections, DiagnosticEngine::Error,
"Linker Script is using PHDR's but not using SECTIONS command")
DIAG(warn_multiple_entry, DiagnosticEngine::Warning,
"multiple entry points specified; the last one takes effect")
DIAG(cannot_set_at_address, DiagnosticEngine::Error,
"Address for section %0 specified with AT cannot be set, as there is a "
"dependency issue, check the map file for more analysis")
Expand Down
4 changes: 4 additions & 0 deletions lib/Config/GeneralOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ void GeneralOptions::setEntry(const std::string &PEntry) { Entry = PEntry; }

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

bool GeneralOptions::isEntryFromCmdLine() const { return EntryFromCmdLine; }

void GeneralOptions::setEntryFromCmdLine() { EntryFromCmdLine = true; }

void GeneralOptions::setTrace(bool EnableTrace) {
DiagEngine->getPrinter()->setTrace(DiagEngine->getPrinter()->TraceFiles);
}
Expand Down
3 changes: 2 additions & 1 deletion lib/LinkerWrapper/GnuLdDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -719,9 +719,10 @@ bool GnuLdDriver::processOptions(llvm::opt::InputArgList &Args) {
T::fatal_internal_errors, T::no_fatal_internal_errors, /*default=*/false);
Config.options().setFatalInternalErrors(enableFatalInternalErrors);

// set up entry point from -e
// set up entry point from -e;
if (llvm::opt::Arg *arg = Args.getLastArg(T::entrypoint)) {
Config.options().setEntry(arg->getValue());
Config.options().setEntryFromCmdLine();
Config.addCommandLine(Table->getOptionName(T::entrypoint), arg->getValue());
}

Expand Down
9 changes: 7 additions & 2 deletions lib/Script/EntryCmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@ void EntryCmd::dump(llvm::raw_ostream &Outs) const {
}

eld::Expected<void> EntryCmd::activate(Module &CurModule) {
if ((!EntrySymbol.empty()) && (!(CurModule.getConfig().options().hasEntry())))
CurModule.getConfig().options().setEntry(EntrySymbol);
GeneralOptions &Options = CurModule.getConfig().options();
if (!EntrySymbol.empty()) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I know it was part of the original code, but do you know if there is any valid case where EntrySymbol may be empty?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Syntactically we can pass ENTRY("") it will result in EntrySymbol.empty(),

lld do gives warning in this case but ld.eld and ld.bfd silently ignores it

ld.lld: warning: cannot find entry symbol _start; not setting start address
  Entry point address:               0x0

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is interesting. We should also give warning for it. Can you please raise an issue for this?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

ENTRY("") is treated as having no ENTRY() at all.
lld gives this warning if we have not specified an entry point at all.

if (Options.hasEntry() && CurModule.getConfig().showLinkerScriptWarnings())
CurModule.getConfig().raise(Diag::warn_multiple_entry);
if (!Options.isEntryFromCmdLine())
Options.setEntry(EntrySymbol);
}
return eld::Expected<void>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#---EntryCmdPrecedence.test-------------- Executable,LS ----------------------#
#BEGIN_COMMENT
# Tests GNU ld entry point precedence rules:
# Case 1: Last ENTRY() in a single script wins over earlier ENTRY() commands.
# Case 2: Command-line -e always overrides any script ENTRY().
# Case 3: Last -e on command line wins over earlier -e.
# Case 4: -e with a raw address overrides script ENTRY().
# Case 5: Across multiple -T scripts, the last ENTRY() activated wins.
# Case 6: -e overrides script ENTRY() regardless of command-line order
# (i.e. -e placed before -T still wins).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please document this as well.

#
# Input scripts fix symbol addresses so entry points can be verified with
# literal address checks (same style as EntryCmd.test):
# two_entries.t : bar=0x1000, foo=0x2000
# script.t : foo=0x1000, bar=0x2000
# entry_foo.t : bar=0x1000, foo=0x2000 (SECTIONS; entry_bar.t is ENTRY only)
#END_COMMENT
#START_TEST
RUN: %clang %clangopts -c %p/Inputs/1.c -o %t1.o -ffunction-sections

# Case 1: ENTRY(bar) then ENTRY(foo) in one script -- foo must win (last wins).
RUN: %link %linkopts %t1.o -T %p/Inputs/two_entries.t -o %t.lastwins.out
RUN: %readelf -h %t.lastwins.out | %filecheck %s --check-prefix=LASTWINS
#LASTWINS: Entry point address: 0x2000

# Case 2: -e bar overrides the script ENTRY(foo) -- command line always wins.
RUN: %link %linkopts %t1.o -T %p/Inputs/two_entries.t -e bar -o %t.cmdline.out
RUN: %readelf -h %t.cmdline.out | %filecheck %s --check-prefix=CMDLINE
#CMDLINE: Entry point address: 0x1000

# Case 3: -e foo -e bar -- bar must win (last -e wins).
RUN: %link %linkopts %t1.o -T %p/Inputs/script.t -e foo -e bar -o %t.lastcmd.out
RUN: %readelf -h %t.lastcmd.out | %filecheck %s --check-prefix=LASTCMD
#LASTCMD: Entry point address: 0x2000

# Case 4: -e 0 overrides script ENTRY(foo) -- entry must be 0x0.
RUN: %link %linkopts %t1.o -T %p/Inputs/script.t -e 0 -o %t.rawaddr.out
RUN: %readelf -h %t.rawaddr.out | %filecheck %s --check-prefix=RAWADDR
#RAWADDR: Entry point address: 0x0

# Case 5: Two separate scripts, ENTRY(bar) then ENTRY(foo) -- last script wins.
RUN: %link %linkopts %t1.o -T %p/Inputs/entry_bar.t -T %p/Inputs/entry_foo.t -o %t.crossscript.out
RUN: %readelf -h %t.crossscript.out | %filecheck %s --check-prefix=CROSSSCRIPT
#CROSSSCRIPT: Entry point address: 0x2000

# Case 6: -e bar placed BEFORE -T -- command line still wins over script ENTRY(foo).
RUN: %link %linkopts %t1.o -e bar -T %p/Inputs/two_entries.t -o %t.eorder.out
RUN: %readelf -h %t.eorder.out | %filecheck %s --check-prefix=EORDER
#EORDER: Entry point address: 0x1000
#END_TEST
53 changes: 53 additions & 0 deletions test/Common/standalone/linkerscript/EntryCmd/EntryCmdWarn.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#---EntryCmdWarn.test-------------------- Executable,LS ----------------------#
#BEGIN_COMMENT
# Tests that -Wlinker-script emits a warning when multiple entry points are
# specified, and that the warning is suppressed without -Wlinker-script.
#
# Warning cases:
# Case 1: Two ENTRY() in one script -- warns once with -Wlinker-script.
# Case 2: -e combined with a single script ENTRY() -- warns with -Wlinker-script.
# Case 3: Multiple -e on command line -- warns once with -Wlinker-script.
# Case 4: Cross-script two ENTRY() -- warns with -Wlinker-script.
# Case 5: Single ENTRY(), no -e -- no warning even with -Wlinker-script.
# Case 6: Two ENTRY() without -Wlinker-script -- no warning.
# Case 7: Three ENTRY() in one script -- warns once per overriding ENTRY().
#END_COMMENT
#START_TEST
RUN: %clang %clangopts -c %p/Inputs/1.c -o %t1.o -ffunction-sections

# Case 1: Two ENTRY() in one script -- exactly one warning.
RUN: %link %linkopts %t1.o -T %p/Inputs/two_entries.t -Wlinker-script -o %t.warn1.out 2>&1 | \
RUN: %filecheck %s --check-prefix=WARN1
#WARN1-COUNT-1: Warning: multiple entry points specified; the last one takes effect
#WARN1-NOT: Warning: multiple entry points specified; the last one takes effect

# Case 2: -e combined with a single script ENTRY() -- warn.
RUN: %link %linkopts %t1.o -T %p/Inputs/script.t -e bar -Wlinker-script -o %t.warn2.out 2>&1 | \
RUN: %filecheck %s --check-prefix=WARN
#WARN: Warning: multiple entry points specified; the last one takes effect

# Case 3: Multiple -e on command line -- exactly one warning.
RUN: %link %linkopts %t1.o -T %p/Inputs/script.t -e foo -e bar -Wlinker-script -o %t.warn3.out 2>&1 | \
RUN: %filecheck %s --check-prefix=WARN3
#WARN3-COUNT-1: Warning: multiple entry points specified; the last one takes effect
#WARN3-NOT: Warning: multiple entry points specified; the last one takes effect

# Case 4: Two ENTRY() across two scripts -- warn.
RUN: %link %linkopts %t1.o -T %p/Inputs/entry_bar.t -T %p/Inputs/entry_foo.t -Wlinker-script -o %t.warn4.out 2>&1 | \
RUN: %filecheck %s --check-prefix=WARN

# Case 5: Single ENTRY(), no -e -- no warning even with -Wlinker-script.
RUN: %link %linkopts %t1.o -T %p/Inputs/script.t -Wlinker-script -o %t.nowarn1.out 2>&1 | \
RUN: %filecheck %s --allow-empty --check-prefix=NOWARN
#NOWARN-NOT: multiple entry points

# Case 6: Two ENTRY() without -Wlinker-script -- no warning.
RUN: %link %linkopts %t1.o -T %p/Inputs/two_entries.t -o %t.nowarn2.out 2>&1 | \
RUN: %filecheck %s --allow-empty --check-prefix=NOWARN

# Case 7: Three ENTRY() in one script -- one warning per overriding ENTRY() (two total).
RUN: %link %linkopts %t1.o -T %p/Inputs/three_entries.t -Wlinker-script -o %t.warn7.out 2>&1 | \
RUN: %filecheck %s --check-prefix=WARN7
#WARN7-COUNT-2: Warning: multiple entry points specified; the last one takes effect
#WARN7-NOT: Warning: multiple entry points specified; the last one takes effect
#END_TEST
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ENTRY(bar)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
ENTRY(foo)
SECTIONS {
. = 0x1000;
.bar : { *(.text.bar) }
. = 0x2000;
.foo : { *(.text.foo) }
}
2 changes: 2 additions & 0 deletions test/Common/standalone/linkerscript/EntryCmd/Inputs/script.t
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@ ENTRY(foo)
SECTIONS {
. = 0x1000;
.foo : { *(.text.foo) }
. = 0x2000;
.bar : { *(.text.bar) }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
ENTRY(bar)
ENTRY(foo)
ENTRY(bar)
SECTIONS {
. = 0x1000;
.bar : { *(.text.bar) }
. = 0x2000;
.foo : { *(.text.foo) }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
ENTRY(bar)
ENTRY(foo)
SECTIONS {
. = 0x1000;
.bar : { *(.text.bar) }
. = 0x2000;
.foo : { *(.text.foo) }
}