Improve the logic of sections insertion in ELF binaries

This commit is contained in:
Romain Thomas
2025-04-19 05:33:34 +02:00
parent cd37b09a52
commit d717340c92
10 changed files with 456 additions and 57 deletions
+14 -1
View File
@@ -562,6 +562,13 @@ class Binary(lief.Binary):
SEGMENT_GAP = 4
class SEC_INSERT_POS(enum.Enum):
AUTO = 0
POST_SECTION = 2
POST_SEGMENT = 1
@property
def type(self) -> Header.CLASS: ...
@@ -581,7 +588,7 @@ class Binary(lief.Binary):
def add(self, arg: DynamicEntry, /) -> DynamicEntry: ...
@overload
def add(self, section: Section, loaded: bool = True) -> Section: ...
def add(self, section: Section, loaded: bool = True, pos: Binary.SEC_INSERT_POS = Binary.SEC_INSERT_POS.AUTO) -> Section: ...
@overload
def add(self, segment: Segment, base: int = 0) -> Segment: ...
@@ -830,6 +837,12 @@ class Binary(lief.Binary):
@property
def is_targeting_android(self) -> bool: ...
@overload
def get_section_idx(self, arg: Section, /) -> Union[int, lief.lief_errors]: ...
@overload
def get_section_idx(self, arg: str, /) -> Union[int, lief.lief_errors]: ...
overlay: memoryview
def relocate_phdr_table(self, type: Binary.PHDR_RELOC = Binary.PHDR_RELOC.AUTO) -> int: ...
+42 -2
View File
@@ -104,6 +104,30 @@ void create<Binary>(nb::module_& m) {
enforcement.
)delim"_doc);
nb::enum_<Binary::SEC_INSERT_POS>(bin, "SEC_INSERT_POS", R"delim(
This enum defines where the content of a newly added section should be
inserted.
)delim"_doc)
.value("AUTO", Binary::SEC_INSERT_POS::AUTO,
"Defer the choice to LIEF"_doc
)
.value("POST_SECTION", Binary::SEC_INSERT_POS::POST_SECTION,
R"doc(
Insert the section after the last valid offset in the **segments** table.
With this choice, the section is inserted after the loaded content but
before any debug information.
)doc"_doc
)
.value("POST_SEGMENT", Binary::SEC_INSERT_POS::POST_SEGMENT,
R"doc(
Insert the section after the last valid offset in the **section** table.
With this choice, the section is inserted at the very end of the binary.
)doc"_doc
)
;
bin
.def_prop_ro("type",
&Binary::type,
@@ -476,14 +500,14 @@ void create<Binary>(nb::module_& m) {
"virtual_address"_a)
.def("add",
nb::overload_cast<const Section&, bool>(&Binary::add),
nb::overload_cast<const Section&, bool, Binary::SEC_INSERT_POS>(&Binary::add),
R"delim(
Add the given :class:`~lief.ELF.Section` to the binary.
If the section does not aim at being loaded in memory,
the ``loaded`` parameter has to be set to ``False`` (default: ``True``)
)delim"_doc,
"section"_a, "loaded"_a = true,
"section"_a, "loaded"_a = true, "pos"_a = Binary::SEC_INSERT_POS::AUTO,
nb::rv_policy::reference_internal)
.def("add",
@@ -734,6 +758,22 @@ void create<Binary>(nb::module_& m) {
R"doc(True if the current is targeting Android)doc"_doc
)
.def("get_section_idx", [] (const Binary& self, const Section& sec) {
return error_or(
nb::overload_cast<const Section&>(&Binary::get_section_idx, nb::const_),
self, sec);
},
R"doc(Find the index of the section given in the first parameter)doc"_doc
)
.def("get_section_idx", [] (const Binary& self, const std::string& name) {
return error_or(
nb::overload_cast<const std::string&>(&Binary::get_section_idx, nb::const_),
self, name);
},
R"doc(Find the index of the section with the name given in the first parameter)doc"_doc
)
.def_prop_rw("overlay",
nb::overload_cast<>(&Binary::overlay, nb::const_),
[] (Binary& self, nb::bytes& bytes) {
+17
View File
@@ -579,6 +579,12 @@
:py:meth:`lief.ELF.Binary.write`
:cpp:func:`LIEF::ELF::Binary::write`
.. |lief-elf-binary-add| lief-api:: lief.ELF.Binary.add()
:py:func:`lief.ELF.Binary.add`
:cpp:func:`LIEF::ELF::Binary::add`
.. |lief-elf-aarch64pauth| lief-api:: lief.ELF.AArch64PAuth
:py:class:`lief.ELF.AArch64PAuth`
@@ -591,6 +597,17 @@
:py:func:`lief.ELF.Relocation.resolve`
:cpp:func:`LIEF::ELF::Relocation::resolve`
.. |lief-elf-segment| lief-api:: lief.ELF.Segment
:rust:struct:`lief::elf::Segment`
:py:class:`lief.ELF.Segment`
:cpp:class:`LIEF::ELF::Segment`
.. |lief-elf-section| lief-api:: lief.ELF.Section
:rust:struct:`lief::elf::Section`
:py:class:`lief.ELF.Section`
:cpp:class:`LIEF::ELF::Section`
.. Mach-O ======================================================================
+2
View File
@@ -33,6 +33,8 @@
:ELF:
* LIEF inserted sections are not compatible with a ``strip`` after the
modification of the binary (see: :ref:`Adding a section/segment <format-elf-section-segment>`)
* Relax the condition over the ``DT_SYMENT`` entry (:issue:`1177`)
* Fix issue when parsing the dynamic table with an invalid offset (bug found
by :github_user:`lebr0nli`)
+119
View File
@@ -130,6 +130,125 @@ file.
:ref:`binary-abstraction`
.. _format-elf-section-segment:
Adding a Section/Segment
************************
The ELF format uses two tables to represent the different slices of the binary:
1. The sections table
2. The segments table
While the sections table offers a detailed view of the binary,
it is primarily needed by the **compiler** and the **linker**. In particular,
this table is not required for **loading** and **executing** an ELF file.
The Android loader enforces the existence of a sections table and requires
certain specific sections but from a loading perspective, this table is not used.
If you intend to modify an ELF file to load additional content into memory
(such as code or data), it is recommended to add a |lief-elf-segment| instead of
a section:
.. tabs::
.. tab:: :fa:`brands fa-python` Python
.. code-block:: python
elf: lief.ELF.Binary = ...
segment = lief.ELF.Segment()
segment.type = lief.ELF.Segment.TYPES.LOAD
segment.content = list(b'Hello World')
new_segment: lief.ELF.Segment = elf.add(segment)
elf.write("new.elf")
.. tab:: :fa:`regular fa-file-code` C++
.. code-block:: cpp
std::unique_ptr<LIEF::ELF::Binary> elf;
LIEF::ELF::Segment segment;
segment.type(LIEF::ELF::Segment::TYPES::LOAD);
segment.content({1, 2, 3});
LIEF::ELF::Segment* new_segment = elf.add(segment);
elf.write("new.elf");
You can also achieve this modification by creating a |lief-elf-section| that will
**implicitly** create an associated ``PT_LOAD`` segment:
.. tabs::
.. tab:: :fa:`brands fa-python` Python
.. code-block:: python
elf: lief.ELF.Binary = ...
section = lief.ELF.Section(".lief_demo")
section.content = list(b'Hello World')
new_section: lief.ELF.Section = elf.add(section, loaded=True)
elf.write("new.elf")
.. tab:: :fa:`regular fa-file-code` C++
.. code-block:: cpp
std::unique_ptr<LIEF::ELF::Binary> elf;
LIEF::ELF::Section section(".lief_demo");
section.content({1, 2, 3});
LIEF::ELF::Section* new_section = elf.add(section, /*loaded=*/true);
elf.write("new.elf");
As mentioned above, the segments table matters from a loading perspective over
the sections table. Therefore, it makes more sense to explicitly add a new
segment rather than adding a section that implicitly adds a segment.
On the other hand, for debugging purposes or specific
tools, one might want to add a **non-loaded** section. In this case, the data
of the section is inserted at the end of the binary right after all the data wrapped
by the segments:
.. tabs::
.. tab:: :fa:`brands fa-python` Python
.. code-block:: python
elf: lief.ELF.Binary = ...
section = lief.ELF.Section(".metadata")
section.content = list(b'version: 1.2.3')
# /!\ Note that loaded is set to False here
# ------------------------------------------
new_section: lief.ELF.Section = elf.add(section, loaded=False)
elf.write("new.elf")
.. tab:: :fa:`regular fa-file-code` C++
.. code-block:: cpp
std::unique_ptr<LIEF::ELF::Binary> elf;
LIEF::ELF::Section section(".metadata");
section.content({1, 2, 3});
LIEF::ELF::Section* new_section = elf.add(section, /*loaded=*/false);
elf.write("new.elf");
See: |lief-elf-binary-add| for the details about the API
Advance Parsing/Writing
***********************
+39 -5
View File
@@ -223,6 +223,25 @@ class LIEF_API Binary : public LIEF::Binary {
SEGMENT_GAP,
};
/// This enum defines where the content of a newly added section should be
/// inserted.
enum class SEC_INSERT_POS {
/// Defer the choice to LIEF
AUTO = 0,
/// Insert the section after the last valid offset in the **segments**
/// table.
///
/// With this choice, the section is inserted after the loaded content but
/// before any debug information.
POST_SEGMENT,
/// Insert the section after the last valid offset in the **section** table.
///
/// With this choice, the section is inserted at the very end of the binary.
POST_SECTION,
};
public:
Binary& operator=(const Binary& ) = delete;
Binary(const Binary& copy) = delete;
@@ -595,14 +614,16 @@ class LIEF_API Binary : public LIEF::Binary {
/// Add a new section in the binary
///
/// @param[in] section The section object to insert
/// @param[in] loaded Boolean value to indicate that section's data must be loaded
/// by a PT_LOAD segment
/// @param[in] section The section object to insert
/// @param[in] loaded Boolean value to indicate that section's data must be loaded
/// by a PT_LOAD segment
/// @param[in] pos Position where to insert the data in the sections table
///
/// @return The section added. The `size` and the `virtual address` might change.
///
/// This function requires a well-formed ELF binary
Section* add(const Section& section, bool loaded = true);
Section* add(const Section& section, bool loaded = true,
SEC_INSERT_POS pos = SEC_INSERT_POS::AUTO);
Section* extend(const Section& section, uint64_t size);
@@ -940,6 +961,18 @@ class LIEF_API Binary : public LIEF::Binary {
return std::distance(sections_.begin(), it);
}
uint8_t ptr_size() const {
switch (type()) {
case Header::CLASS::ELF32:
return sizeof(uint32_t);
case Header::CLASS::ELF64:
return sizeof(uint64_t);
default:
return 0;
}
return 0;
}
static bool classof(const LIEF::Binary* bin) {
return bin->format() == Binary::FORMATS::ELF ||
bin->format() == Binary::FORMATS::OAT;
@@ -1077,12 +1110,13 @@ class LIEF_API Binary : public LIEF::Binary {
LIEF_LOCAL Segment* extend_segment(const Segment& segment, uint64_t size);
template<bool LOADED>
LIEF_LOCAL Section* add_section(const Section& section);
LIEF_LOCAL Section* add_section(const Section& section, SEC_INSERT_POS pos);
std::vector<Symbol*> symtab_dyn_symbols() const;
LIEF_LOCAL std::string shstrtab_name() const;
LIEF_LOCAL Section* add_frame_section(const Section& sec);
LIEF_LOCAL Section* add_section(std::unique_ptr<Section> sec);
LIEF_LOCAL LIEF::Binary::functions_t tor_functions(DynamicEntry::TAG tag) const;
+31 -3
View File
@@ -972,14 +972,14 @@ result<uint64_t> Binary::get_function_address(const std::string& func_name, bool
return (*it_symbol)->value();
}
Section* Binary::add(const Section& section, bool loaded) {
Section* Binary::add(const Section& section, bool loaded, SEC_INSERT_POS pos) {
if (section.is_frame()) {
return add_frame_section(section);
}
if (loaded) {
return add_section<true>(section);
return add_section<true>(section, pos);
}
return add_section<false>(section);
return add_section<false>(section, pos);
}
@@ -3132,6 +3132,34 @@ bool Binary::is_targeting_android() const {
return false;
}
Section* Binary::add_section(std::unique_ptr<Section> sec) {
Section* sec_ptr = sec.get();
const auto it_new_sec_place = std::find_if(
sections_.begin(), sections_.end(), [sec_ptr] (const std::unique_ptr<Section>& S) {
return S->file_offset() > sec_ptr->file_offset();
});
if (it_new_sec_place == sections_.end()) {
sections_.push_back(std::move(sec));
} else {
size_t idx = std::distance(sections_.begin(), it_new_sec_place);
for (size_t i = 0; i < sections_.size(); ++i) {
const uint32_t link = sections_[i]->link();
if (link >= idx) {
sections_[i]->link(link + 1);
}
}
if (header_.section_name_table_idx() >= idx) {
header_.section_name_table_idx(header_.section_name_table_idx() + 1);
}
sections_.insert(it_new_sec_place, std::move(sec));
}
return sec_ptr;
}
std::ostream& Binary::print(std::ostream& os) const {
os << "Header" << '\n';
+91 -40
View File
@@ -32,6 +32,36 @@
namespace LIEF {
namespace ELF {
inline void init_alignment(Segment& segment, uintptr_t pagesize, uintptr_t ptrsz) {
if (segment.alignment() > 0) {
return;
}
switch (segment.type()) {
case Segment::TYPE::LOAD:
segment.alignment(pagesize);
break;
case Segment::TYPE::PHDR:
case Segment::TYPE::DYNAMIC:
case Segment::TYPE::TLS:
segment.alignment(ptrsz);
break;
case Segment::TYPE::NOTE:
case Segment::TYPE::GNU_EH_FRAME:
segment.alignment(sizeof(uint32_t));
break;
case Segment::TYPE::GNU_RELRO:
segment.alignment(1);
break;
case Segment::TYPE::GNU_STACK:
segment.alignment(0x10);
break;
default:
segment.alignment(ptrsz);
break;
}
}
// ===============
// ARM Relocations
// ===============
@@ -394,9 +424,9 @@ Segment* Binary::add_segment<Header::FILE_TYPE::EXEC>(const Segment& segment, ui
new_segment->physical_size(segmentsize);
new_segment->virtual_size(segmentsize);
if (new_segment->alignment() == 0) {
new_segment->alignment(psize);
}
init_alignment(*new_segment, psize, this->ptr_size());
new_segment->datahandler_ = datahandler_.get();
DataHandler::Node new_node{new_segment->file_offset(), new_segment->physical_size(),
@@ -432,12 +462,11 @@ Segment* Binary::add_segment<Header::FILE_TYPE::EXEC>(const Segment& segment, ui
// =======================
template<>
Segment* Binary::add_segment<Header::FILE_TYPE::DYN>(const Segment& segment, uint64_t base) {
const auto psize = static_cast<uint64_t>(get_pagesize(*this));
const auto psize = (uint64_t)get_pagesize(*this);
const auto ptr_size = this->ptr_size();
/*const uint64_t new_phdr_offset = */ relocate_phdr_table_auto();
span<const uint8_t> content_ref = segment.content();
std::vector<uint8_t> content{content_ref.data(), std::end(content_ref)};
std::vector<uint8_t> content = as_vector(segment.content());
auto new_segment = std::make_unique<Segment>(segment);
new_segment->datahandler_ = datahandler_.get();
@@ -446,29 +475,32 @@ Segment* Binary::add_segment<Header::FILE_TYPE::DYN>(const Segment& segment, uin
DataHandler::Node::SEGMENT};
datahandler_->add(new_node);
const uint64_t last_offset_sections = last_offset_section();
init_alignment(*new_segment, psize, ptr_size);
const uint64_t last_offset_segments = last_offset_segment();
const uint64_t last_offset = std::max<uint64_t>(last_offset_sections, last_offset_segments);
const uint64_t last_offset_aligned = align(last_offset, psize);
const uint64_t last_offset = last_offset_segments;
const uint64_t last_offset_aligned = align(last_offset, 0x10);
if (base == 0) {
base = align(next_virtual_address(), psize);
}
uint64_t segmentsize = align(content.size(), 0x10);
const uint64_t delta = last_offset_aligned + segmentsize - last_offset;
shift_sections(last_offset, delta);
new_segment->file_offset(last_offset_aligned);
new_segment->virtual_address(new_segment->file_offset() + base);
new_segment->physical_address(new_segment->virtual_address());
uint64_t segmentsize = align(content.size(), 0x10);
//uint64_t segmentsize = content.size();
new_segment->handler_size_ = content.size();
new_segment->physical_size(segmentsize);
new_segment->virtual_size(segmentsize);
if (new_segment->alignment() == 0) {
new_segment->alignment(psize);
}
// Patch SHDR
Header& header = this->header();
const uint64_t new_section_hdr_offset = new_segment->file_offset() + new_segment->physical_size();
header.section_headers_offset(new_section_hdr_offset);
header.section_headers_offset(header.section_headers_offset() + delta);
auto alloc = datahandler_->make_hole(last_offset_aligned, new_segment->physical_size());
@@ -486,7 +518,9 @@ Segment* Binary::add_segment<Header::FILE_TYPE::DYN>(const Segment& segment, uin
[&new_segment] (const std::unique_ptr<Segment>& s) {
return s->type() == new_segment->type();
});
Segment* seg_ptr = new_segment.get();
if (it_new_segment_place == segments_.rend()) {
segments_.push_back(std::move(new_segment));
} else {
@@ -563,14 +597,12 @@ Segment* Binary::extend_segment<Segment::TYPE::LOAD>(const Segment& segment, uin
return segment_to_extend.get();
}
template<>
Section* Binary::add_section<true>(const Section& section) {
LIEF_DEBUG("Adding section '{}' as LOADED", section.name());
// Create a Segment:
inline Segment seg_for_section(const Section& section) {
Segment new_segment;
// Create the segment associated with the section
span<const uint8_t> content_ref = section.content();
new_segment.content({std::begin(content_ref), std::end(content_ref)});
new_segment.content(as_vector(content_ref));
new_segment.type(Segment::TYPE::LOAD);
new_segment.virtual_address(section.virtual_address());
@@ -589,8 +621,20 @@ Section* Binary::add_section<true>(const Section& section) {
if (section.has(Section::FLAGS::EXECINSTR)) {
new_segment.add(Segment::FLAGS::X);
}
return new_segment;
}
Segment* segment_added = add(new_segment);
template<>
Section* Binary::add_section</*loaded=*/true>(const Section& section,
SEC_INSERT_POS pos)
{
LIEF_DEBUG("Adding section '{}' as LOADED", section.name());
if (pos != SEC_INSERT_POS::AUTO && pos != SEC_INSERT_POS::POST_SEGMENT) {
LIEF_ERR("Unsupported position for inserting loaded section");
return nullptr;
}
Segment* segment_added = add(seg_for_section(section));
if (segment_added == nullptr) {
LIEF_ERR("Can't add a LOAD segment of the section");
return nullptr;
@@ -615,16 +659,14 @@ Section* Binary::add_section<true>(const Section& section) {
segment_added->sections_.push_back(new_section.get());
header().numberof_sections(header().numberof_sections() + 1);
Section* sec_ptr = new_section.get();
sections_.push_back(std::move(new_section));
return sec_ptr;
return add_section(std::move(new_section));
}
// Add a non-loaded section
template<>
Section* Binary::add_section<false>(const Section& section) {
Section* Binary::add_section</*loaded=*/false>(const Section& section,
SEC_INSERT_POS pos)
{
auto new_section = std::make_unique<Section>(section);
new_section->datahandler_ = datahandler_.get();
@@ -634,7 +676,20 @@ Section* Binary::add_section<false>(const Section& section) {
const uint64_t last_offset_sections = last_offset_section();
const uint64_t last_offset_segments = last_offset_segment();
const uint64_t last_offset = std::max<uint64_t>(last_offset_sections, last_offset_segments);
uint64_t last_offset = 0;
switch (pos) {
case SEC_INSERT_POS::AUTO:
case SEC_INSERT_POS::POST_SEGMENT:
last_offset = last_offset_segments;
break;
case SEC_INSERT_POS::POST_SECTION:
last_offset = std::max(last_offset_segments, last_offset_sections);
break;
}
const uint64_t delta = section.size();
shift_sections(last_offset, delta);
auto alloc = datahandler_->make_hole(last_offset, section.size());
if (!alloc) {
@@ -646,17 +701,13 @@ Section* Binary::add_section<false>(const Section& section) {
new_section->size(section.size());
// Copy original content in the data handler
span<const uint8_t> content_ref = section.content();
new_section->content({std::begin(content_ref), std::end(content_ref)});
header().numberof_sections(header().numberof_sections() + 1);
new_section->content(as_vector(section.content()));
Header& header = this->header();
const uint64_t new_section_hdr_offset = new_section->offset() + new_section->size();
header.numberof_sections(header.numberof_sections() + 1);
const uint64_t new_section_hdr_offset = header.section_headers_offset() + delta;
header.section_headers_offset(new_section_hdr_offset);
Section* sec_ptr = new_section.get();
sections_.push_back(std::move(new_section));
return sec_ptr;
return add_section(std::move(new_section));
}
template<class ELF_T>
+8 -4
View File
@@ -962,7 +962,8 @@ class LIEF_LOCAL ExeLayout : public Layout {
binary_->remove(*string_names_section, /* clear */ true);
Section sec_str_section(sec_name, Section::TYPE::STRTAB);
sec_str_section.content(std::vector<uint8_t>(raw_shstrtab_.size()));
binary_->add(sec_str_section, /* loaded */ false);
binary_->add(sec_str_section, /*loaded=*/false,
/*pos=*/Binary::SEC_INSERT_POS::POST_SECTION);
// Default behavior: push_back => index = binary_->sections_.size() - 1
hdr.section_name_table_idx(binary_->sections_.size() - 1);
@@ -1567,7 +1568,8 @@ class LIEF_LOCAL ExeLayout : public Layout {
Section strtab{".strtab", Section::TYPE::STRTAB};
strtab.content(raw_strtab_);
strtab.alignment(1);
Section* new_strtab = binary_->add(strtab, /* loaded */ false);
Section* new_strtab = binary_->add(
strtab, /*loaded=*/false, /*pos=*/Binary::SEC_INSERT_POS::POST_SECTION);
strtab_idx = binary_->sections().size() - 1;
@@ -1624,7 +1626,8 @@ class LIEF_LOCAL ExeLayout : public Layout {
symtab.entry_size(sizeof_sym);
symtab.alignment(8);
symtab.link(strtab_idx);
Section* new_symtab = binary_->add(symtab, /* loaded */ false);
Section* new_symtab = binary_->add(
symtab, /*loaded=*/false, /*pos=*/Binary::SEC_INSERT_POS::POST_SECTION);
if (new_symtab == nullptr) {
LIEF_ERR("Can't add a new .symbtab section");
return make_error_code(lief_errors::build_error);
@@ -1664,7 +1667,8 @@ class LIEF_LOCAL ExeLayout : public Layout {
Section section{sec_name, Section::TYPE::NOTE};
section += Section::FLAGS::ALLOC;
Section* section_added = binary_->add(section, /*loaded */ false);
Section* section_added = binary_->add(
section, /*loaded=*/false, /*pos=*/Binary::SEC_INSERT_POS::POST_SECTION);
if (section_added == nullptr) {
LIEF_ERR("Can't add SHT_NOTE section");
return make_error_code(lief_errors::build_error);
+93 -2
View File
@@ -7,10 +7,11 @@ import stat
import subprocess
import sys
import pytest
import shutil
from pathlib import Path
from subprocess import Popen
from utils import is_linux, glibc_version, get_sample
from utils import is_linux, glibc_version, get_sample, has_private_samples
SAMPLE_DIR = pathlib.Path(os.getenv("LIEF_SAMPLES_DIR", ""))
@@ -259,7 +260,6 @@ def test_issue_970(tmp_path: Path):
assert svd_0.auxiliary_symbols[0].name == "libcudart.so.12"
assert svd_1.auxiliary_symbols[0].name == "libcudart.so.12"
def test_issue_1121(tmp_path: Path):
elf = lief.ELF.parse(get_sample("ELF/issue_1121.elf"))
elf.get_symbol("main").name = "main_test"
@@ -269,3 +269,94 @@ def test_issue_1121(tmp_path: Path):
new = lief.ELF.parse(out)
assert new.has_symbol("main_test")
@pytest.mark.skipif(not has_private_samples(), reason="needs private samples")
def test_smart_insert_1(tmp_path: Path):
"""
The purpose of this test is to make sure that when we have a binary with debug
info (or not) and we insert a new section/segment, the section is inserted
prior the debug info so that stripping the binary works
"""
input_path = Path(get_sample("private/ELF/libclang-cpp.so.20.1"))
elf = lief.ELF.parse(input_path)
section = lief.ELF.Section(".lief_test")
section.content = list(b"This is a test")
elf.add(section)
output = tmp_path / input_path.name
elf.write(output.as_posix())
new_elf = lief.ELF.parse(output)
sec = new_elf.get_section(".lief_test")
assert new_elf.get_section_idx(sec) == 27
if is_linux():
llvm_strip = shutil.which("llvm-strip")
assert llvm_strip is not None
print(f"Using llvm-strip: {llvm_strip}")
popen_args = {
"universal_newlines": True,
"stdout": subprocess.PIPE,
"stderr": subprocess.STDOUT,
}
args = [
llvm_strip,
output.as_posix()
]
with Popen(args, **popen_args) as proc: # type: ignore[call-overload]
stdout, _ = proc.communicate(10)
print("stdout:", stdout)
assert proc.returncode == 0
assert len(stdout) == 0
elf_strip = lief.ELF.parse(output)
lief_test_section: lief.ELF.Section = elf_strip.get_section(".lief_test")
assert lief_test_section is not None
print(bytes(lief_test_section.content))
assert bytes(lief_test_section.content) == b'This is a test\x00\x00'
@pytest.mark.skipif(not has_private_samples(), reason="needs private samples")
def test_smart_insert_2(tmp_path: Path):
input_path = Path(get_sample("private/ELF/libhwui.so"))
elf = lief.ELF.parse(input_path)
section = lief.ELF.Section(".lief_section_to_strip")
section.content = list(b"The content of this section needs to be removed")
elf.add(section, loaded=False)
output = tmp_path / input_path.name
elf.write(output.as_posix())
new_elf = lief.ELF.parse(output)
sec = new_elf.get_section(".lief_section_to_strip")
assert new_elf.get_section_idx(sec) == 25
if is_linux():
llvm_strip = shutil.which("llvm-strip")
assert llvm_strip is not None
print(f"Using llvm-strip: {llvm_strip}")
popen_args = {
"universal_newlines": True,
"stdout": subprocess.PIPE,
"stderr": subprocess.STDOUT,
}
args = [
llvm_strip,
output.as_posix()
]
with Popen(args, **popen_args) as proc: # type: ignore[call-overload]
stdout, _ = proc.communicate(10)
print("stdout:", stdout)
assert proc.returncode == 0
assert len(stdout) == 0
elf_strip = lief.ELF.parse(output)
lief_test_section: lief.ELF.Section = elf_strip.get_section(".lief_section_to_strip")
assert lief_test_section is None