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
10 changes: 10 additions & 0 deletions include/eld/Config/GeneralOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ class GeneralOptions {

typedef llvm::StringMap<uint64_t> AddressMapType;

enum class SeparateSegmentKind { None, Code };

enum StripSymbolMode {
KeepAllSymbols,
StripTemporaries,
Expand Down Expand Up @@ -654,6 +656,12 @@ class GeneralOptions {

void setROSegment(bool R = false) { Rosegment = R; }

SeparateSegmentKind getSeparateSegmentKind() const {
return SeparateSegments;
}

void setSeparateSegmentKind(SeparateSegmentKind K) { SeparateSegments = K; }

bool verifyLink() const { return Verify; }

void setVerifyLink(bool V = true) { Verify = V; }
Expand Down Expand Up @@ -1214,6 +1222,8 @@ class GeneralOptions {
bool Savetemps = false; // -save-temps
std::optional<std::string> SaveTempsDir; // -save-temps=
bool Rosegment = false; // merge read only with readonly/execute segments.
SeparateSegmentKind SeparateSegments =
SeparateSegmentKind::None; // -z separate-code
std::vector<std::string>
UnparsedLTOOptions; // Unparsed -flto-options, to pass to plugin.
uint32_t LTOOptions = 0; // -flto-options
Expand Down
4 changes: 3 additions & 1 deletion include/eld/Driver/GnuLinkerOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -872,7 +872,9 @@ defm dash_z
"\t\t\t-z=now : Enables immediate binding\n"
"\t\t\t-z=nocopyreloc : Disables Copy Relocation"
"\t\t\t-z=text : Do not permit relocations in read-only segments (default)\n"
"\t\t\t-z=notext : Allow relocations in read-only segments\n">,
"\t\t\t-z=notext : Allow relocations in read-only segments\n"
"\t\t\t-z=separate-code : Create separate code segment\n"
"\t\t\t-z=no-separate-code : Do not create separate code segment\n">,
MetaVarName<"<extended-opts>">,
Group<grp_extendedopts>;
def no_align_segments : Flag<["--"], "no-align-segments">,
Expand Down
2 changes: 2 additions & 0 deletions include/eld/Input/ZOption.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ class ZOption {
NoDelete,
NoExecStack,
NoGnuStack,
SeparateCode,
NoseparateCode,
Copy link
Member

Choose a reason for hiding this comment

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

Would this be better?

Suggested change
NoseparateCode,
NoSeparateCode,

NoRelro,
Now,
Origin,
Expand Down
2 changes: 1 addition & 1 deletion include/eld/Object/ScriptMemoryRegion.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class ScriptMemoryRegion {

// We used to have only one function called getAddr and there
// was no way to differentiate between a call to virtual address
// or physical address. Now we need this seperate to support
// or physical address. Now we need this separate to support
// ALIGN_WITH_INPUT
uint64_t getVirtualAddr(OutputSectionEntry *O);

Expand Down
6 changes: 6 additions & 0 deletions lib/Config/GeneralOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ bool GeneralOptions::addZOption(const ZOption &POption) {
case ZOption::NoGnuStack:
NoGnuStack = true;
break;
case eld::ZOption::SeparateCode:
setSeparateSegmentKind(SeparateSegmentKind::Code);
break;
case eld::ZOption::NoseparateCode:
setSeparateSegmentKind(SeparateSegmentKind::None);
break;
case ZOption::Global:
BGlobal = true;
break;
Expand Down
2 changes: 1 addition & 1 deletion lib/LayoutMap/LayoutInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ bool LayoutInfo::isSectionDetailedInfoAvailable(ELFSection *Section) {
if (Section->isIgnore())
return false;

// These sections are handled seperately and they dont follow
// These sections are handled separately and they dont follow
// the same path of merging
switch (Section->getKind()) {
case LDFileFormat::Discard:
Expand Down
4 changes: 4 additions & 0 deletions lib/LinkerWrapper/GnuLdDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,10 @@ bool GnuLdDriver::processOptions(llvm::opt::InputArgList &Args) {
ZKind = eld::ZOption::NoExecStack;
else if (ZOpt == "nognustack")
ZKind = eld::ZOption::NoGnuStack;
else if (ZOpt == "separate-code")
ZKind = eld::ZOption::SeparateCode;
else if (ZOpt == "noseparate-code")
ZKind = eld::ZOption::NoseparateCode;
else if (ZOpt == "execstack")
ZKind = eld::ZOption::ExecStack;
else if (ZOpt == "nodelete") {
Expand Down
2 changes: 1 addition & 1 deletion lib/Support/StringUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ std::string ReplaceString(std::string &Subject, const std::string &Search,
return Subject;
}

/// \brief Split a string with multiple names seperated by comma.
/// \brief Split a string with multiple names separated by comma.
/// \returns a vector of strings.
std::vector<std::string> split(const std::string &S, char Seperator) {
std::vector<std::string> Output;
Expand Down
25 changes: 23 additions & 2 deletions lib/Target/CreateProgramHeaders.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,25 @@ bool GNULDBackend::createProgramHdrs() {

reset_state();

auto SeparateKind = config().options().getSeparateSegmentKind();
bool ShouldSeparate =
!linkerScriptHasSectionsCommand &&
Copy link
Member

Choose a reason for hiding this comment

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

It seems that the -z separate-code option only takes affect when the linker script does not have SECTIONS command. Should we report a warning if this option is used along with the SECTIONS command?

(SeparateKind != GeneralOptions::SeparateSegmentKind::None);

auto ShouldPageAlignSegment = [&](bool IsExec) {
if (config().options().alignSegmentsToPage())
return true;
if (!ShouldSeparate)
return false;
if (!prevOut)
return false;
auto PrevSegIt = _segmentsForSection.find(prevOut);
if (PrevSegIt == _segmentsForSection.end() || PrevSegIt->second.empty())
return false;
bool PrevExec = (PrevSegIt->second.front()->flag() & llvm::ELF::PF_X);
return PrevExec != IsExec;
};

Copy link
Contributor

Choose a reason for hiding this comment

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

Can we rename this to something better since the lambda does nore than aligning segments to a page ?

Why does aligning segments to a page not work with -z seperate-code ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can we rename this to something better since the lambda does nore than aligning segments to a page ?

If it does anything more than determine whether or not segments should be page aligned then I did something wrong. Taking another look, I am not seeing that it does anything other than that. What am I missing? What name do you suggest?

Why does aligning segments to a page not work with -z seperate-code ?

I am not sure if I understand your question. It does "work". Starting code segments at page aligned offsets ensures that they are not co-resident on the same page as other segments. That is the point of this lambda function: when transitioning to/from a code segment to another kind of segment, with -z separate-code the next segment should be page aligned. That is my understanding anyways

Copy link
Contributor

@quic-seaswara quic-seaswara Jan 16, 2026

Choose a reason for hiding this comment

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

The lambda checks for the variety of things

  • align segments to a page
  • checks for the last section added to the segment and sees if it has execute permissions (SeperateSegmentKind for code)

Should this be called DoPageAlignment or NeedSeperateSegmentOrPageAlign ?

Copy link
Contributor Author

@quic-areg quic-areg Jan 16, 2026

Choose a reason for hiding this comment

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

checks for the last section added to the segment and sees if it has execute permissions (SeperateSegmentKind for code)

Because it is relevant to determine if we ShouldPageAlignSegments?

It checks if we should page align segments based on:

  1. --align-segments/no-align-segments
  2. if we have a linker script
  3. -z separate-code. This requires looking at transitions to/from executable segments

Should this be called DoPageAlignment or NeedSeperateSegmentOrPageAlign

I don't see how these are better names. They sound like (worse) synonyms to me.

if (load_ehdr)
setNeedEhdr();

Expand Down Expand Up @@ -359,6 +378,7 @@ bool GNULDBackend::createProgramHdrs() {
}

cur_flag = getSegmentFlag(cur->getFlags());
bool CurIsExec = cur_flag & llvm::ELF::PF_X;

if (linkerScriptHasMemoryCommand && (*out)->epilog().hasRegion())
cur_mem_region = (*out)
Expand Down Expand Up @@ -445,7 +465,8 @@ bool GNULDBackend::createProgramHdrs() {
}

if (isCurAlloc && (createPT_LOAD || last_section_needs_new_segment)) {
if (config().options().alignSegmentsToPage())
bool ShouldPageAlign = ShouldPageAlignSegment(CurIsExec);
if (ShouldPageAlign)
alignAddress(vma, segAlign);
if (cur->isFixedAddr() && (vma != cur->addr())) {
config().raise(Diag::cannot_set_at_address) << cur->name();
Expand Down Expand Up @@ -483,7 +504,7 @@ bool GNULDBackend::createProgramHdrs() {

// FIXME : remove this option alignSegmentsToPage
// Handle the case without any linker scripts,
if (config().options().alignSegmentsToPage())
if (ShouldPageAlign)
alignAddress(pma, segAlign);

cur->setPaddr(pma);
Expand Down
2 changes: 1 addition & 1 deletion test/ARM/standalone/PartialLinkEXIDX/PartialLinkEXIDX.test
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#---PartialEXIDX.test--------------------------- PartialLink -----------------#
#BEGIN_COMMENT
#Linker should not create seperate exidx sections even if link order is set.
#Linker should not create separate exidx sections even if link order is set.
#END_COMMENT
#START_TEST
RUN: %clang %clangopts -c %p/Inputs/1.c -o %t1.1.o
Expand Down
40 changes: 40 additions & 0 deletions test/lld/ELF/separate-segments.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o

## -z noseparate-code. All PT_LOAD can have overlapping p_offset
## ranges at runtime.
# RUN: %link -pie --no-align-segments --rosegment %t.o -o %t
# RUN: llvm-readelf -l %t | FileCheck --check-prefix=NONE %s
# NONE: LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x000200 0x000200 R E 0x1000
# NONE-NEXT: LOAD 0x000200 0x0000000000000200 0x0000000000000200 0x000044 0x000044 R 0x1000
# NONE-NEXT: LOAD 0x000244 0x0000000000000244 0x0000000000000244 0x000001 0x000001 R E 0x1000
# NONE-NEXT: LOAD 0x000245 0x0000000000000245 0x0000000000000245 0x000001 0x000001 R 0x1000
# NONE-NEXT: LOAD 0x000248 0x0000000000000248 0x0000000000000248 0x0000f1 0x0000f1 RW 0x1000

## -z separate-code makes text segment (RX) separate.
## The two RW can have overlapping p_offset ranges at runtime.
# RUN: %link -pie --no-align-segments --rosegment %t.o -z separate-code -o %t
# RUN: llvm-readelf -l %t | FileCheck --check-prefix=CODE %s
# CODE: LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x000200 0x000200 R E 0x1000
# CODE-NEXT: LOAD 0x001000 0x0000000000001000 0x0000000000001000 0x000044 0x000044 R 0x1000
# CODE-NEXT: LOAD 0x002000 0x0000000000002000 0x0000000000002000 0x000001 0x000001 R E 0x1000
# CODE-NEXT: LOAD 0x003000 0x0000000000003000 0x0000000000003000 0x000001 0x000001 R 0x1000
# CODE-NEXT: LOAD 0x003008 0x0000000000003008 0x0000000000003008 0x0000f1 0x0000f1 RW 0x1000

## TODO:
## -z separate-loadable-segments makes all segments separate.
# %link -pie --no-align-segments --rosegment %t.o -z separate-loadable-segments -o %t
# llvm-readelf -l %t | FileCheck --check-prefix=ALL %s
# ALL: LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x000200 0x000200 R E 0x1000
# ALL-NEXT: LOAD 0x001000 0x0000000000001000 0x0000000000001000 0x000044 0x000044 R 0x1000
# ALL-NEXT: LOAD 0x002000 0x0000000000002000 0x0000000000002000 0x000001 0x000001 R E 0x1000
# ALL-NEXT: LOAD 0x003000 0x0000000000003000 0x0000000000003000 0x000001 0x000001 R 0x1000
# ALL-NEXT: LOAD 0x004000 0x0000000000004000 0x0000000000004000 0x0000f1 0x0000f1 RW 0x1000

nop

.section .rodata,"a"
.byte 0

.data
.byte 0
Loading