diff --git a/docs/userguide/documentation/layout.rst b/docs/userguide/documentation/layout.rst index cd673e550..4f64681ae 100644 --- a/docs/userguide/documentation/layout.rst +++ b/docs/userguide/documentation/layout.rst @@ -277,6 +277,28 @@ 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 specified address to the output section. + You may use this option multiple times to place multiple sections at specific addresses. + +-Ttext=org + Same as ``--section-start`` with ``.text`` as the section name. + +-Tdata=org + Same as ``--section-start`` with ``.data`` as the section name. + +-Tbss=org + Same as ``--section-start`` with ``.bss`` as the section name. + +-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. diff --git a/include/eld/Config/GeneralOptions.h b/include/eld/Config/GeneralOptions.h index 20e19dac7..0838508a7 100644 --- a/include/eld/Config/GeneralOptions.h +++ b/include/eld/Config/GeneralOptions.h @@ -746,6 +746,13 @@ class GeneralOptions { const std::optional &imageBase() const { return ImageBase; } void setImageBase(uint64_t Value) { ImageBase = Value; } + + const std::optional &textSegment() const { return TextSegment; } + void setTextSegment(uint64_t Value) { TextSegment = Value; } + const std::optional &rodataSegment() const { return RodataSegment; } + void setRodataSegment(uint64_t Value) { RodataSegment = Value; } + const std::optional &ldataSegment() const { return LdataSegment; } + void setLdataSegment(uint64_t Value) { LdataSegment = Value; } /// entry point const std::string &entry() const; @@ -1377,6 +1384,9 @@ class GeneralOptions { std::vector LTOOutputFile; bool BCompactDyn = false; // z,compactdyn std::optional ImageBase; // --image-base=value + std::optional TextSegment; + std::optional RodataSegment; + std::optional LdataSegment; std::string Entry; SymbolRenameMap SymbolRenames; AddressMapType AddressMap; diff --git a/include/eld/Driver/GnuLinkerOptions.td b/include/eld/Driver/GnuLinkerOptions.td index 6f08f0db0..e89c79c25 100644 --- a/include/eld/Driver/GnuLinkerOptions.td +++ b/include/eld/Driver/GnuLinkerOptions.td @@ -225,6 +225,16 @@ defm Ttext_segment "Specify an address for the .text-segment segment">, MetaVarName<"
">, Group; +defm Trodata_segment + : dashEqWithOpt<"Trodata-segment", "Trodata-segment", "Trodata_segment", + "Specify an address for the .rodata-segment segment">, + MetaVarName<"
">, + Group; +defm Tldata_segment + : dashEqWithOpt<"Tldata-segment", "Tldata-segment", "Tldata_segment", + "Specify an address for the .ldata-segment segment">, + MetaVarName<"
">, + Group; defm Tdata : smDash<"Tdata", "Tdata", "Specify an address for the .data section">, MetaVarName<"
">, diff --git a/lib/LinkerWrapper/GnuLdDriver.cpp b/lib/LinkerWrapper/GnuLdDriver.cpp index 8b34e5c2f..d050aae19 100644 --- a/lib/LinkerWrapper/GnuLdDriver.cpp +++ b/lib/LinkerWrapper/GnuLdDriver.cpp @@ -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)) + Config.options().setTextSegment(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().setRodataSegment(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().setLdataSegment(addr); + } + // --dynamic-list for (auto *Arg : Args.filtered(T::dynamic_list)) Config.options().getDynList().emplace(Arg->getValue()); diff --git a/lib/Target/GNULDBackend.cpp b/lib/Target/GNULDBackend.cpp index ba25876b8..4d4820970 100644 --- a/lib/Target/GNULDBackend.cpp +++ b/lib/Target/GNULDBackend.cpp @@ -3847,6 +3847,8 @@ bool GNULDBackend::symbolNeedsCopyReloc(const Relocation &pReloc, } uint64_t GNULDBackend::getImageBase(bool HasInterp, bool LoadEHdr) const { + if (auto TextSegment = config().options().textSegment()) + return *TextSegment; if (auto ImageBase = config().options().imageBase()) return *ImageBase; return m_pInfo->startAddr( diff --git a/test/Common/standalone/CommandLine/Ttext/Inputs/1.c b/test/Common/standalone/CommandLine/Ttext/Inputs/1.c new file mode 100644 index 000000000..e34078f88 --- /dev/null +++ b/test/Common/standalone/CommandLine/Ttext/Inputs/1.c @@ -0,0 +1,3 @@ +int data_var = 1; +int bss_var; +int foo() { return 0; } diff --git a/test/Common/standalone/CommandLine/Ttext/Inputs/force-sections.t b/test/Common/standalone/CommandLine/Ttext/Inputs/force-sections.t new file mode 100644 index 000000000..63d236cc8 --- /dev/null +++ b/test/Common/standalone/CommandLine/Ttext/Inputs/force-sections.t @@ -0,0 +1,5 @@ +SECTIONS { + .text : { *(.text*) } + .data : { *(.data*) *(.sdata*) } + .bss : { *(.bss*) *(.sbss*) } +} diff --git a/test/Common/standalone/CommandLine/Ttext/Ttext.test b/test/Common/standalone/CommandLine/Ttext/Ttext.test new file mode 100644 index 000000000..4613738d5 --- /dev/null +++ b/test/Common/standalone/CommandLine/Ttext/Ttext.test @@ -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 diff --git a/test/Common/standalone/CommandLine/TtextSegment/Inputs/1.c b/test/Common/standalone/CommandLine/TtextSegment/Inputs/1.c new file mode 100644 index 000000000..9fe07f82f --- /dev/null +++ b/test/Common/standalone/CommandLine/TtextSegment/Inputs/1.c @@ -0,0 +1 @@ +int foo() { return 0; } diff --git a/test/Common/standalone/CommandLine/TtextSegment/TtextSegment.test b/test/Common/standalone/CommandLine/TtextSegment/TtextSegment.test new file mode 100644 index 000000000..494a1553f --- /dev/null +++ b/test/Common/standalone/CommandLine/TtextSegment/TtextSegment.test @@ -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 diff --git a/test/Common/standalone/VersionScriptAnonymousMixed/Inputs/1.c b/test/Common/standalone/VersionScriptAnonymousMixed/Inputs/1.c new file mode 100644 index 000000000..cd529553b --- /dev/null +++ b/test/Common/standalone/VersionScriptAnonymousMixed/Inputs/1.c @@ -0,0 +1,2 @@ +void foo() {} +void bar() {} diff --git a/test/Common/standalone/VersionScriptAnonymousMixed/Inputs/vs_mixed b/test/Common/standalone/VersionScriptAnonymousMixed/Inputs/vs_mixed new file mode 100644 index 000000000..a75a2110c --- /dev/null +++ b/test/Common/standalone/VersionScriptAnonymousMixed/Inputs/vs_mixed @@ -0,0 +1,6 @@ +VER_1.0 { + global: foo; +}; +{ + global: bar; +}; diff --git a/test/Common/standalone/VersionScriptAnonymousMixed/VersionScriptAnonymousMixed.test b/test/Common/standalone/VersionScriptAnonymousMixed/VersionScriptAnonymousMixed.test new file mode 100644 index 000000000..29c72214a --- /dev/null +++ b/test/Common/standalone/VersionScriptAnonymousMixed/VersionScriptAnonymousMixed.test @@ -0,0 +1,14 @@ +UNSUPPORTED: x86 +#---VersionScriptAnonymousMixed.test--------------------- SharedLibrary,VS------------------# +#BEGIN_COMMENT +# Test that an error is emitted when an anonymous version definition +# is used in combination with other named version definitions +# in a version script. +# See: https://github.com/qualcomm/eld/issues/1124 +#END_COMMENT + +RUN: %clang %clangopts -c -fpic %p/Inputs/1.c -o %t1.o +RUN: %not %link %linkopts -shared -o %t1.so %t1.o --version-script=%p/Inputs/vs_mixed 2>&1 | %filecheck %s + +CHECK: Error: {{.*}}: anonymous version definition is used in combination with other version definitions + \ No newline at end of file diff --git a/test/Hexagon/standalone/CommandLine/SectionStart/SectionStart.test b/test/Hexagon/standalone/CommandLine/SectionStart/SectionStart.test new file mode 100644 index 000000000..2c2763d25 --- /dev/null +++ b/test/Hexagon/standalone/CommandLine/SectionStart/SectionStart.test @@ -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 diff --git a/test/x86_64/standalone/CommandLine/TldataSegment/Inputs/1.c b/test/x86_64/standalone/CommandLine/TldataSegment/Inputs/1.c new file mode 100644 index 000000000..1e0adcfe5 --- /dev/null +++ b/test/x86_64/standalone/CommandLine/TldataSegment/Inputs/1.c @@ -0,0 +1,2 @@ +int data_var = 1; +int foo() { return 0; } diff --git a/test/x86_64/standalone/CommandLine/TldataSegment/TldataSegment.test b/test/x86_64/standalone/CommandLine/TldataSegment/TldataSegment.test new file mode 100644 index 000000000..ef5c9d764 --- /dev/null +++ b/test/x86_64/standalone/CommandLine/TldataSegment/TldataSegment.test @@ -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 diff --git a/test/x86_64/standalone/CommandLine/TrodataSegment/Inputs/1.c b/test/x86_64/standalone/CommandLine/TrodataSegment/Inputs/1.c new file mode 100644 index 000000000..149675885 --- /dev/null +++ b/test/x86_64/standalone/CommandLine/TrodataSegment/Inputs/1.c @@ -0,0 +1,2 @@ +const int rodata_var = 1; +int foo() { return 0; } diff --git a/test/x86_64/standalone/CommandLine/TrodataSegment/TrodataSegment.test b/test/x86_64/standalone/CommandLine/TrodataSegment/TrodataSegment.test new file mode 100644 index 000000000..796f35af1 --- /dev/null +++ b/test/x86_64/standalone/CommandLine/TrodataSegment/TrodataSegment.test @@ -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