mirror of
https://github.com/lief-project/LIEF
synced 2026-06-08 15:30:44 +00:00
Add support for LC_FUNCTION_VARIANTS/LC_FUNCTION_VARIANT_FIXUPS
Note that this support is partially incomplete especially for writing back Mach-O binaries.
This commit is contained in:
@@ -39,6 +39,8 @@
|
||||
#include <LIEF/MachO/FatBinary.hpp>
|
||||
#include <LIEF/MachO/FilesetCommand.hpp>
|
||||
#include <LIEF/MachO/FunctionStarts.hpp>
|
||||
#include <LIEF/MachO/FunctionVariants.hpp>
|
||||
#include <LIEF/MachO/FunctionVariantFixups.hpp>
|
||||
#include <LIEF/MachO/AtomInfo.hpp>
|
||||
#include <LIEF/MachO/Header.hpp>
|
||||
#include <LIEF/MachO/IndirectBindingInfo.hpp>
|
||||
@@ -106,6 +108,8 @@ void init_objects(nb::module_& m) {
|
||||
CREATE(DyldBindingInfo, m);
|
||||
CREATE(ExportInfo, m);
|
||||
CREATE(FunctionStarts, m);
|
||||
CREATE(FunctionVariants, m);
|
||||
CREATE(FunctionVariantFixups, m);
|
||||
CREATE(AtomInfo, m);
|
||||
CREATE(CodeSignature, m);
|
||||
CREATE(CodeSignatureDir, m);
|
||||
|
||||
@@ -23,6 +23,8 @@ target_sources(pyLIEF PRIVATE
|
||||
pyFatBinary.cpp
|
||||
pyFilesetCommand.cpp
|
||||
pyFunctionStarts.cpp
|
||||
pyFunctionVariants.cpp
|
||||
pyFunctionVariantFixups.cpp
|
||||
pyHeader.cpp
|
||||
pyIndirectBindingInfo.cpp
|
||||
pyLinkerOptHint.cpp
|
||||
|
||||
@@ -41,6 +41,8 @@
|
||||
#include "LIEF/MachO/EncryptionInfo.hpp"
|
||||
#include "LIEF/MachO/ExportInfo.hpp"
|
||||
#include "LIEF/MachO/FunctionStarts.hpp"
|
||||
#include "LIEF/MachO/FunctionVariants.hpp"
|
||||
#include "LIEF/MachO/FunctionVariantFixups.hpp"
|
||||
#include "LIEF/MachO/LinkEdit.hpp"
|
||||
#include "LIEF/MachO/LinkerOptHint.hpp"
|
||||
#include "LIEF/MachO/AtomInfo.hpp"
|
||||
@@ -464,6 +466,24 @@ void create<Binary>(nb::module_& m) {
|
||||
"Return the binary's " RST_CLASS_REF(lief.MachO.AtomInfo) " if any, or None"_doc,
|
||||
nb::rv_policy::reference_internal)
|
||||
|
||||
.def_prop_ro("has_function_variants",
|
||||
&Binary::has_function_variants,
|
||||
"``True`` if the binary has a ``LC_FUNCTION_VARIANTS`` command"_doc)
|
||||
|
||||
.def_prop_ro("function_variants",
|
||||
nb::overload_cast<>(&Binary::function_variants),
|
||||
"Return ``LC_FUNCTION_VARIANTS`` command"_doc,
|
||||
nb::rv_policy::reference_internal)
|
||||
|
||||
.def_prop_ro("has_function_variant_fixups",
|
||||
&Binary::has_function_variants,
|
||||
"``True`` if the binary has a ``LC_FUNCTION_VARIANT_FIXUPS`` command"_doc)
|
||||
|
||||
.def_prop_ro("function_variant_fixups",
|
||||
nb::overload_cast<>(&Binary::function_variants),
|
||||
"Return ``LC_FUNCTION_VARIANT_FIXUPS`` command"_doc,
|
||||
nb::rv_policy::reference_internal)
|
||||
|
||||
.def("virtual_address_to_offset",
|
||||
[] (const Binary& self, uint64_t va) {
|
||||
return error_or(&Binary::virtual_address_to_offset, self, va);
|
||||
|
||||
@@ -34,7 +34,7 @@ void create<BindingInfo>(nb::module_& m) {
|
||||
|
||||
This class does not represent a structure that exists in the Mach-O format
|
||||
specifications but it provides a *view* of a binding operation that is performed
|
||||
by the Dyld binding bytecode (`LC_DYLD_INFO`) or the Dyld chained fixups (`DYLD_CHAINED_FIXUPS`)
|
||||
by the Dyld binding bytecode (``LC_DYLD_INFO``) or the Dyld chained fixups (``DYLD_CHAINED_FIXUPS``)
|
||||
|
||||
See: :class:`~lief.MachO.ChainedBindingInfo`, :class:`~lief.MachO.DyldBindingInfo`
|
||||
)delim"_doc)
|
||||
|
||||
@@ -51,6 +51,7 @@ void create<ExportInfo>(nb::module_& m) {
|
||||
.value(PY_ENUM(ExportInfo::FLAGS::WEAK_DEFINITION))
|
||||
.value(PY_ENUM(ExportInfo::FLAGS::REEXPORT))
|
||||
.value(PY_ENUM(ExportInfo::FLAGS::STUB_AND_RESOLVER))
|
||||
.value(PY_ENUM(ExportInfo::FLAGS::STATIC_RESOLVER))
|
||||
#undef PY_ENUM
|
||||
;
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/* Copyright 2017 - 2025 R. Thomas
|
||||
* Copyright 2017 - 2025 Quarkslab
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <nanobind/stl/string.h>
|
||||
|
||||
#include "LIEF/MachO/FunctionVariantFixups.hpp"
|
||||
|
||||
#include "MachO/pyMachO.hpp"
|
||||
#include "nanobind/extra/stl/lief_span.h"
|
||||
|
||||
namespace LIEF::MachO::py {
|
||||
|
||||
template<>
|
||||
void create<FunctionVariantFixups>(nb::module_& m) {
|
||||
nb::class_<FunctionVariantFixups, LoadCommand>(m, "FunctionVariantFixups",
|
||||
R"delim(
|
||||
)delim"_doc)
|
||||
|
||||
.def_prop_rw("data_offset",
|
||||
nb::overload_cast<>(&FunctionVariantFixups::data_offset, nb::const_),
|
||||
nb::overload_cast<uint32_t>(&FunctionVariantFixups::data_offset),
|
||||
"Offset in the binary where the payload starts"_doc)
|
||||
|
||||
.def_prop_rw("data_size",
|
||||
nb::overload_cast<>(&FunctionVariantFixups::data_size, nb::const_),
|
||||
nb::overload_cast<uint32_t>(&FunctionVariantFixups::data_size),
|
||||
"Size of the payload"_doc)
|
||||
|
||||
.def_prop_ro("content",
|
||||
nb::overload_cast<>(&FunctionVariantFixups::content, nb::const_),
|
||||
"Payload content"_doc)
|
||||
|
||||
LIEF_DEFAULT_STR(FunctionVariantFixups);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
/* Copyright 2017 - 2025 R. Thomas
|
||||
* Copyright 2017 - 2025 Quarkslab
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <nanobind/stl/string.h>
|
||||
#include <nanobind/stl/vector.h>
|
||||
|
||||
#include "LIEF/MachO/FunctionVariants.hpp"
|
||||
#include "pyIterator.hpp"
|
||||
|
||||
#include "MachO/pyMachO.hpp"
|
||||
#include "nanobind/extra/stl/lief_span.h"
|
||||
|
||||
namespace LIEF::MachO::py {
|
||||
|
||||
using RuntimeTableEntry = FunctionVariants::RuntimeTableEntry;
|
||||
using RuntimeTable = FunctionVariants::RuntimeTable;
|
||||
using FLAGS = FunctionVariants::RuntimeTableEntry::FLAGS;
|
||||
using KIND = FunctionVariants::RuntimeTable::KIND;
|
||||
|
||||
template<>
|
||||
void create<FunctionVariants>(nb::module_& m) {
|
||||
using namespace LIEF::py;
|
||||
nb::class_<FunctionVariants, LoadCommand> cmd(m, "FunctionVariants",
|
||||
R"doc(
|
||||
Class representing the ``LC_FUNCTION_VARIANTS`` load command.
|
||||
|
||||
Introduced publicly in ``dyld-1284.13`` (April 2025), this command supports
|
||||
**function multiversioning**, the ability to associate multiple implementations
|
||||
of the same function, each optimized for a specific platform, architecture,
|
||||
or runtime context.
|
||||
|
||||
At runtime, the system dispatches the most appropriate variant based on
|
||||
hardware capabilities or execution environment.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
FUNCTION_VARIANT_TABLE(my_function,
|
||||
{ (void*)my_function$Rosetta, "rosetta" }, // Rosetta translation
|
||||
{ (void*)my_function$Haswell, "haswell" }, // Haswell-optimized
|
||||
{ (void*)my_function$Base, "default" } // Default fallback
|
||||
);
|
||||
)doc"_doc);
|
||||
|
||||
|
||||
|
||||
nb::class_<RuntimeTableEntry> runtime_entry(cmd, "RuntimeTableEntry",
|
||||
R"doc(
|
||||
This class exposes information about a given implementation.
|
||||
)doc"_doc
|
||||
);
|
||||
|
||||
#define FUNCTION_VARIANT_FLAG(X, _, __) .value(MachO::to_string(FLAGS::X), FLAGS::X)
|
||||
nb::enum_<FLAGS>(runtime_entry, "FLAGS")
|
||||
#include "LIEF/MachO/FunctionVariants/Arm64.def"
|
||||
#include "LIEF/MachO/FunctionVariants/PerProcess.def"
|
||||
#include "LIEF/MachO/FunctionVariants/SystemWide.def"
|
||||
#include "LIEF/MachO/FunctionVariants/X86_64.def"
|
||||
.value("UNKNOWN", FLAGS::UNKNOWN)
|
||||
;
|
||||
#undef FUNCTION_VARIANT_FLAG
|
||||
|
||||
runtime_entry
|
||||
.def_prop_ro("impl", &RuntimeTableEntry::impl,
|
||||
R"doc(
|
||||
The relative address of the implementation or an index if
|
||||
:attr:`~.another_table` is set.
|
||||
)doc"_doc
|
||||
)
|
||||
|
||||
.def_prop_ro("another_table", &RuntimeTableEntry::another_table,
|
||||
R"doc(
|
||||
Indicates whether :attr:`~.impl` refers to an entry in another runtime table,
|
||||
rather than a direct function implementation address.
|
||||
)doc"_doc
|
||||
)
|
||||
|
||||
.def_prop_ro("flag_bit_nums", &RuntimeTableEntry::flag_bit_nums,
|
||||
R"doc(
|
||||
The ``flagBitNums`` value as a slice of bytes
|
||||
)doc"_doc
|
||||
)
|
||||
|
||||
.def_prop_ro("flags", &RuntimeTableEntry::flags,
|
||||
R"doc(
|
||||
Return the **interpreted** :attr:`~.flag_bit_nums`
|
||||
)doc"_doc
|
||||
)
|
||||
LIEF_DEFAULT_STR(RuntimeTableEntry);
|
||||
|
||||
nb::class_<RuntimeTable> runtime_table(cmd, "RuntimeTable",
|
||||
R"doc(
|
||||
Represents a runtime table of function variants sharing a common namespace
|
||||
(referred to internally as ``FunctionVariantsRuntimeTable`` in ``dyld``).
|
||||
|
||||
Each table holds multiple :class:`~.RuntimeTableEntry` instances that map to
|
||||
function implementations optimized for a given :class:`~.RuntimeTable.KIND`.
|
||||
)doc"_doc
|
||||
);
|
||||
|
||||
nb::enum_<KIND>(runtime_table, "KIND",
|
||||
R"doc(
|
||||
Enumeration describing the namespace or category of a function variant.
|
||||
|
||||
Each :class:`~.RuntimeTable` is associated with one :class:`~.RuntimeTable.KIND`,
|
||||
which indicates the domain or context under which its variant entries
|
||||
should be considered valid or applicable.
|
||||
|
||||
These categories map to the runtime dispatch logic used by ``dyld``
|
||||
when selecting the optimal function variant.
|
||||
)doc"_doc)
|
||||
.value("UNKNOWN", KIND::UNKNOWN,
|
||||
"Fallback/default kind when the category is not recognized"_doc
|
||||
)
|
||||
|
||||
.value("PER_PROCESS", KIND::PER_PROCESS,
|
||||
"Variants that apply on a per-process basis"_doc
|
||||
)
|
||||
|
||||
.value("SYSTEM_WIDE", KIND::SYSTEM_WIDE,
|
||||
"Variants that are selected based on system-wide capabilities or configurations"_doc
|
||||
)
|
||||
|
||||
.value("ARM64", KIND::ARM64,
|
||||
"Variants optimized for the ARM64 architecture."_doc
|
||||
)
|
||||
|
||||
.value("X86_64", KIND::X86_64,
|
||||
"Variants optimized for the x86-64 architecture."_doc
|
||||
)
|
||||
;
|
||||
|
||||
init_ref_iterator<RuntimeTable::it_entries>(runtime_table, "it_entries");
|
||||
|
||||
runtime_table
|
||||
.def_prop_ro("kind", &RuntimeTable::kind,
|
||||
"Kind of the runtime table"_doc
|
||||
)
|
||||
|
||||
.def_prop_ro("offset", &RuntimeTable::offset,
|
||||
"Original offset in the payload"_doc
|
||||
)
|
||||
|
||||
.def_prop_ro("entries", nb::overload_cast<>(&RuntimeTable::entries),
|
||||
"Iterator over the different :class:`~.RuntimeTableEntry` entries"_doc,
|
||||
nb::keep_alive<0, 1>()
|
||||
)
|
||||
LIEF_DEFAULT_STR(RuntimeTable);
|
||||
;
|
||||
|
||||
init_ref_iterator<FunctionVariants::it_runtime_table>(cmd, "it_runtime_table");
|
||||
|
||||
cmd
|
||||
.def_prop_rw("data_offset",
|
||||
nb::overload_cast<>(&FunctionVariants::data_offset, nb::const_),
|
||||
nb::overload_cast<uint32_t>(&FunctionVariants::data_offset),
|
||||
"Offset in the binary where the payload starts"_doc)
|
||||
|
||||
.def_prop_rw("data_size",
|
||||
nb::overload_cast<>(&FunctionVariants::data_size, nb::const_),
|
||||
nb::overload_cast<uint32_t>(&FunctionVariants::data_size),
|
||||
"Size of the payload"_doc)
|
||||
|
||||
.def_prop_ro("content",
|
||||
nb::overload_cast<>(&FunctionVariants::content, nb::const_),
|
||||
"Payload content"_doc)
|
||||
|
||||
.def_prop_ro("runtime_table", nb::overload_cast<>(&FunctionVariants::runtime_table),
|
||||
R"doc(
|
||||
Iterator over the different :class:`~.RuntimeTable` entries located in the content
|
||||
of this ``__LINKEDIT`` command
|
||||
)doc"_doc,
|
||||
nb::keep_alive<0, 1>()
|
||||
)
|
||||
|
||||
LIEF_DEFAULT_STR(FunctionVariants);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -31,14 +31,14 @@ void create<RelocationFixup>(nb::module_& m) {
|
||||
|
||||
This class extends :class:`lief.Relocation` (and :class:`lief.MachO.Relocation`) in which
|
||||
:attr:`~lief.Relocation.address` is set to the absolute virtual address
|
||||
where the relocation must take place (e.g. `0x10000d270`).
|
||||
where the relocation must take place (e.g. ``0x10000d270``).
|
||||
|
||||
On the other hand, :attr:`~lief.MachO.RelocationFixup.target` contains the value
|
||||
that should be set at :attr:`~lief.Relocation.address` if the
|
||||
imagebase is :attr:`~lief.Binary.imagebase` (e.g. `0x1000073a8`).
|
||||
imagebase is :attr:`~lief.Binary.imagebase` (e.g. ``0x1000073a8``).
|
||||
|
||||
If the Mach-O loader chooses another base address (like 0x7ff100000), it must set
|
||||
`0x10000d270` to `0x7ff1073a8`.
|
||||
If the Mach-O loader chooses another base address (like ``0x7ff100000``), it must set
|
||||
``0x10000d270`` to ``0x7ff1073a8``.
|
||||
)delim"_doc)
|
||||
|
||||
.def_prop_rw("target",
|
||||
|
||||
@@ -32,12 +32,12 @@ void create<Routine>(nb::module_& m) {
|
||||
Class that represents the ``LC_ROUTINE/LC_ROUTINE64`` commands.
|
||||
Accodring to the Mach-O ``loader.h`` documentation:
|
||||
|
||||
> The routines command contains the address of the dynamic shared library
|
||||
> initialization routine and an index into the module table for the module
|
||||
> that defines the routine. Before any modules are used from the library the
|
||||
> dynamic linker fully binds the module that defines the initialization routine
|
||||
> and then calls it. This gets called before any module initialization
|
||||
> routines (used for C++ static constructors) in the library.
|
||||
The routines command contains the address of the dynamic shared library
|
||||
initialization routine and an index into the module table for the module
|
||||
that defines the routine. Before any modules are used from the library the
|
||||
dynamic linker fully binds the module that defines the initialization routine
|
||||
and then calls it. This gets called before any module initialization
|
||||
routines (used for C++ static constructors) in the library.
|
||||
)delim"_doc)
|
||||
|
||||
.def_prop_rw("init_address",
|
||||
|
||||
Reference in New Issue
Block a user