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
26 changes: 26 additions & 0 deletions docs/userguide/documentation/layout.rst
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,32 @@ alignment specified in :code:`ALIGN`.
There are various linker command-line options for setting output section
VMA: ``-Tbss``, ``-Tdata``, ``-Ttext`` and ``--section-start``.

--section-start=sectionname=org
Assigns the absolute address org to the named output section.
This option can be used multiple times to set addresses for different sections.
If the same section name is specified more than once, the last address is used.

-Ttext=org
Same as ``--section-start`` with ``.text`` as the section name.
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.

What if there is no .text section ?

If the output does not contain a ``.text`` section, this has no effect.

-Tdata=org
Same as ``--section-start`` with ``.data`` as the section name.
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.

Same as previous, what if there is no data section ?

If the output does not contain a ``.data`` section, this has no effect.

-Tbss=org
Same as ``--section-start`` with ``.bss`` as the section name.
If the output does not contain a ``.bss`` section, this has no effect.

-Ttext-segment=org
When creating an ELF executable, it sets the address of the first byte of the text segment.

-Trodata-segment=org
When creating an ELF executable or shared object for a target where the read-only data is in its own segment separate from the executable text, it sets the address of the first byte of the read-only data segment.

-Tldata-segment=org
When creating an ELF executable or shared object for the x86-64 medium memory model, it sets the address of the first byte of the ldata segment.

When both the linker script and the command line specify an output-section address,
the command-line option takes precedence and overrides the script's explicit address.

Expand Down
10 changes: 10 additions & 0 deletions include/eld/Config/GeneralOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,13 @@ class GeneralOptions {
const std::optional<uint64_t> &imageBase() const { return ImageBase; }

void setImageBase(uint64_t Value) { ImageBase = Value; }

const std::optional<uint64_t> &getTextSegmentAddress() const { return TextSegment; }
void setTextSegmentAddress(uint64_t Value) { TextSegment = Value; }
const std::optional<uint64_t> &getRodataSegmentAddress() const { return RodataSegment; }
void setRodataSegmentAddress(uint64_t Value) { RodataSegment = Value; }
const std::optional<uint64_t> &getLdataSegmentAddress() const { return LdataSegment; }
void setLdataSegmentAddress(uint64_t Value) { LdataSegment = Value; }

/// entry point
const std::string &entry() const;
Expand Down Expand Up @@ -1377,6 +1384,9 @@ class GeneralOptions {
std::vector<std::string> LTOOutputFile;
bool BCompactDyn = false; // z,compactdyn
std::optional<uint64_t> ImageBase; // --image-base=value
std::optional<uint64_t> TextSegment;
std::optional<uint64_t> RodataSegment;
std::optional<uint64_t> LdataSegment;
std::string Entry;
SymbolRenameMap SymbolRenames;
AddressMapType AddressMap;
Expand Down
10 changes: 10 additions & 0 deletions include/eld/Driver/GnuLinkerOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,16 @@ defm Ttext_segment
"Specify an address for the .text-segment segment">,
MetaVarName<"<address>">,
Group<grp_scriptopts>;
defm Trodata_segment
: dashEqWithOpt<"Trodata-segment", "Trodata-segment", "Trodata_segment",
"Set address of rodata segment">,
MetaVarName<"<address>">,
Group<grp_scriptopts>;
defm Tldata_segment
: dashEqWithOpt<"Tldata-segment", "Tldata-segment", "Tldata_segment",
"Set address of ldata segment">,
MetaVarName<"<address>">,
Group<grp_scriptopts>;
defm Tdata
: smDash<"Tdata", "Tdata", "Specify an address for the .data section">,
MetaVarName<"<address>">,
Expand Down
21 changes: 21 additions & 0 deletions lib/LinkerWrapper/GnuLdDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,27 @@ bool GnuLdDriver::processOptions(llvm::opt::InputArgList &Args) {
Config.options().addressMap().insert(std::make_pair(".text", addr));
}

// -Ttext-segment=value
if (llvm::opt::Arg *arg = Args.getLastArg(T::Ttext_segment)) {
uint64_t addr = 0;
if (!llvm::StringRef(arg->getValue()).getAsInteger(0, addr))
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.

We should report an error if the argument is incorrect for the new options added.

Config.options().setTextSegmentAddress(addr);
}

// -Trodata-segment=value
if (llvm::opt::Arg *arg = Args.getLastArg(T::Trodata_segment)) {
uint64_t addr = 0;
if (!llvm::StringRef(arg->getValue()).getAsInteger(0, addr))
Config.options().setRodataSegmentAddress(addr);
}

// -Tldata-segment=value
if (llvm::opt::Arg *arg = Args.getLastArg(T::Tldata_segment)) {
uint64_t addr = 0;
if (!llvm::StringRef(arg->getValue()).getAsInteger(0, addr))
Config.options().setLdataSegmentAddress(addr);
}

// --dynamic-list
for (auto *Arg : Args.filtered(T::dynamic_list))
Config.options().getDynList().emplace(Arg->getValue());
Expand Down
2 changes: 2 additions & 0 deletions lib/Target/GNULDBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4060,6 +4060,8 @@ bool GNULDBackend::symbolNeedsCopyReloc(const Relocation &pReloc,
}

uint64_t GNULDBackend::getImageBase(bool HasInterp, bool LoadEHdr) const {
if (auto TextSegment = config().options().getTextSegmentAddress())
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.

Can you please check what is the precedence between --image-base and -Ttext-segment in GNU ld?

return *TextSegment;
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.

Does -Ttext-segment override linker script assignment ?

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.

I tested this, when both -Ttext-segment and a linker script address assignment are specified, the linker script takes precedence. Is this the expected behavior, or should -Ttext-segment always override the linker script ?

Copy link
Copy Markdown
Author

@sai18022001 sai18022001 May 9, 2026

Choose a reason for hiding this comment

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

Pinging on this - wanted to confirm the expected behavior before making any further changes to GNULDBackend.cpp.
Based on GNU ld behavior, the linker script takes precedence over -Ttext-segment when an explicit address is assigned in the script. The current implementation follows this behavior. Please let me know if a different behavior is expected for ELD.

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.

Thanks for confirming. Please go ahead.

if (auto ImageBase = config().options().imageBase())
return *ImageBase;
return m_pInfo->startAddr(
Expand Down
3 changes: 3 additions & 0 deletions test/Common/standalone/CommandLine/Ttext/Inputs/1.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
int data_var = 1;
int bss_var;
int foo() { return 0; }
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
SECTIONS {
.text : { *(.text*) }
.data : { *(.data*) *(.sdata*) }
.bss : { *(.bss*) *(.sbss*) }
}
16 changes: 16 additions & 0 deletions test/Common/standalone/CommandLine/Ttext/Ttext.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#---Ttext.test--------------------------- Executable,LS -----------------#
#BEGIN_COMMENT
# This checks for options -Ttext, -Tdata and -Tbss that are handled in the linker.
#END_COMMENT
#START_TEST
RUN: %clang %clangopts -c %p/Inputs/1.c -o %t1.o
RUN: %link %linkopts %t1.o -o %t1.out -Ttext=0x10000000
RUN: %readelf -S %t1.out -W | %filecheck %s --check-prefix=CHECK-TEXT
CHECK-TEXT: .text {{.*}} {{0*}}10000000
RUN: %link %linkopts %t1.o -T %p/Inputs/force-sections.t -o %t2.out -Tdata=0x20000000
RUN: %readelf -S %t2.out -W | %filecheck %s --check-prefix=CHECK-DATA
CHECK-DATA: .data {{.*}} {{0*}}20000000
RUN: %link %linkopts %t1.o -T %p/Inputs/force-sections.t -o %t3.out -Tbss=0x30000000
RUN: %readelf -S %t3.out -W | %filecheck %s --check-prefix=CHECK-BSS
CHECK-BSS: .bss {{.*}} {{0*}}30000000
#END_TEST
1 change: 1 addition & 0 deletions test/Common/standalone/CommandLine/TtextSegment/Inputs/1.c
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
int foo() { return 0; }
10 changes: 10 additions & 0 deletions test/Common/standalone/CommandLine/TtextSegment/TtextSegment.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#---TtextSegment.test--------------------------- Executable,LS -----------------#
#BEGIN_COMMENT
# This checks for option -Ttext-segment that is handled in the linker.
#END_COMMENT
#START_TEST
RUN: %clang %clangopts -c %p/Inputs/1.c -o %t1.o
RUN: %link %linkopts %t1.o -o %t1.out -Ttext-segment=0x10000000
RUN: %readelf -l %t1.out -W | %filecheck %s --check-prefix=CHECK-TEXT-SEGMENT
CHECK-TEXT-SEGMENT: LOAD {{.*}} 0x{{0*}}10000000
#END_TEST
10 changes: 10 additions & 0 deletions test/Hexagon/standalone/CommandLine/SectionStart/SectionStart.test
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.

Why is this test hexagon specific?

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#---SectionStart.test--------------------------- Executable,LS -----------------#
#BEGIN_COMMENT
# This checks for option --section-start that is being handled in the linker.
#END_COMMENT
#START_TEST
RUN: %clang %clangopts -c %p/Inputs/1.c -o %t1.1.o
RUN: %link %linkopts %t1.1.o -o %t2.out --section-start .text=0xF0000000
RUN: %readelf -s %t2.out -W | %filecheck %s
#CHECK: f0000000
#END_TEST
2 changes: 2 additions & 0 deletions test/x86_64/standalone/CommandLine/TldataSegment/Inputs/1.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
int data_var = 1;
int foo() { return 0; }
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#---TldataSegment.test--------------------------- Executable,LS -----------------#
#BEGIN_COMMENT
# This checks for option -Tldata-segment that is handled in the linker.
#END_COMMENT
#START_TEST
RUN: %clang %clangopts -c %p/Inputs/1.c -o %t1.o
RUN: %link %linkopts %t1.o -o %t1.out -Tldata-segment=0x30000000
RUN: %readelf -l %t1.out -W | %filecheck %s --check-prefix=CHECK-LDATA-SEGMENT
CHECK-LDATA-SEGMENT: LOAD {{.*}} 0x{{0*}}30000000
#END_TEST
2 changes: 2 additions & 0 deletions test/x86_64/standalone/CommandLine/TrodataSegment/Inputs/1.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
const int rodata_var = 1;
int foo() { return 0; }
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#---TrodataSegment.test--------------------------- Executable,LS -----------------#
#BEGIN_COMMENT
# This checks for option -Trodata-segment that is handled in the linker.
#END_COMMENT
#START_TEST
RUN: %clang %clangopts -c %p/Inputs/1.c -o %t1.o
RUN: %link %linkopts %t1.o -o %t1.out -Trodata-segment=0x20000000
RUN: %readelf -l %t1.out -W | %filecheck %s --check-prefix=CHECK-RODATA-SEGMENT
CHECK-RODATA-SEGMENT: LOAD {{.*}} 0x{{0*}}20000000
#END_TEST
Loading