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
27 changes: 24 additions & 3 deletions src/runtime_src/core/common/api/elf_int.h
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,10 @@ class elf_impl
// Final kernel objects built from m_kernel_args_map and m_kernel_to_subkernels_map
std::vector<elf::kernel> m_kernels;

// Map for custom sections that are not tied to specific kernels or groups
// key - custom section name, value - custom section data
std::map<std::string, detail::span<const char>> m_global_custom_section_map;

/* Patcher related types and data - common between all platforms */
// Aliases for patcher types
using patcher_config = xrt_core::elf_patcher::patcher_config;
Expand All @@ -285,9 +289,9 @@ class elf_impl
// Protected constructor - takes already-loaded ELFIO and platform
elf_impl(ELFIO::elfio&& elfio, elf::platform platform);

// Parse .group sections in the ELF file and populate all maps
// Parse sections in the ELF and populate internal maps
void
parse_group_sections();
parse_sections();

private:
////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -333,6 +337,19 @@ class elf_impl
void
parse_single_group_section(const ELFIO::section* section);

// Parse custom sections and populate corresponding maps
void
parse_custom_sections(const std::vector<uint32_t>& custom_section_ids);

// Helper function to add custom section to kernel (instance_name is empty)
// or to an instance.
void
add_custom_section_to_kernel_or_instance(
const std::string& kernel_name,
const std::string& instance_name,
const std::string& sec_name,
detail::span<const char> data);

public:
virtual ~elf_impl() = default;

Expand Down Expand Up @@ -434,10 +451,14 @@ class elf_impl
return nullptr;
}


// Get the ERT command opcode in ELF flow
virtual ert_cmd_opcode
get_ert_opcode() const = 0;

// Get custom section data by name
// Returns span of custom section data
detail::span<const char>
get_custom_section(const std::string& name);
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

elf_impl::get_custom_section() is a read-only accessor but is declared/defined non-const. Making it const would better match the rest of the getter-style API, allow calling it on const elf_impl, and communicate that it does not mutate internal state.

Suggested change
get_custom_section(const std::string& name);
get_custom_section(const std::string& name) const;

Copilot uses AI. Check for mistakes.
};

} // namespace xrt
Expand Down
194 changes: 180 additions & 14 deletions src/runtime_src/core/common/api/xrt_elf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,10 @@ class elf::kernel_impl
xrt_core::xclbin::kernel_properties m_properties;
std::vector<elf::kernel::instance> m_instances;

// Map for custom sections of a kernel
// key - custom section name, value - custom section data
std::map<std::string, detail::span<const char>> m_custom_section_map;

Comment on lines +247 to +250
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

kernel_impl stores custom section data as detail::span<const char> in m_custom_section_map. The span points into ELFIO::section data owned by elf_impl::m_elfio, but xrt::elf::kernel objects can outlive the parent xrt::elf (they hold independent shared_ptr<kernel_impl>). This makes kernel::get_custom_section() able to return a dangling view (use-after-free) once the parent xrt::elf is destroyed. Consider storing an owning buffer for each custom section (e.g., std::vector<char>/shared buffer) or redesigning so kernel_impl/instance_impl retain safe access to the backing ELF data without creating ownership cycles (e.g., keep indices + weak_ptr to elf_impl and fail if expired).

Copilot uses AI. Check for mistakes.
xrt_core::xclbin::kernel_properties
construct_properties(const std::string& name) const
{
Expand Down Expand Up @@ -301,11 +305,29 @@ class elf::kernel_impl
{
return {m_properties, m_args};
}

void
add_custom_section(const std::string& section_name, detail::span<const char> section_data)
{
m_custom_section_map[section_name] = section_data;
}

detail::span<const char>
get_custom_section(const std::string& section_name) const
{
if (auto it = m_custom_section_map.find(section_name); it != m_custom_section_map.end())
return it->second;

throw std::runtime_error("Cannot get custom section " + section_name + " data, section not found for kernel " + m_name);
}
};

class elf::kernel::instance_impl
{
std::string m_name;
// Map for custom sections of an instance
// key - custom section name, value - custom section data
std::map<std::string, detail::span<const char>> m_custom_section_map;
Comment on lines +328 to +330
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

instance_impl also stores custom section data as detail::span<const char>, which is a non-owning view into elf_impl::m_elfio section memory. Since xrt::elf::kernel::instance objects can be copied out and outlive the parent xrt::elf, instance::get_custom_section() can return a dangling span after the xrt::elf is destroyed. Please change storage to an owning representation (or a safe indirection back to elf_impl with lifetime checks) so the returned span never references freed memory.

Copilot uses AI. Check for mistakes.

public:
explicit
Expand All @@ -318,6 +340,21 @@ class elf::kernel::instance_impl
{
return m_name;
}

void
add_custom_section(const std::string& section_name, detail::span<const char> section_data)
{
m_custom_section_map[section_name] = section_data;
}

detail::span<const char>
get_custom_section(const std::string& section_name) const
{
if (auto it = m_custom_section_map.find(section_name); it != m_custom_section_map.end())
return it->second;

throw std::runtime_error("Cannot get custom section " + section_name + " data, section not found for instance " + m_name);
}
};

////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -351,8 +388,9 @@ get_symbol_from_symtab(uint32_t sym_index) const

if (!symbols.get_symbol(sym_index, info.name, value, size, bind,
info.type, info.section_index, other))
throw std::runtime_error("Unable to find symbol in .symtab section with index: " +
std::to_string(sym_index));
throw std::runtime_error(
"Unable to find symbol in .symtab section with index: " +
std::to_string(sym_index));

return info;
}
Expand Down Expand Up @@ -412,12 +450,14 @@ get_kernel_subkernel_from_symtab(uint32_t sym_index)
// Get subkernel symbol - must be of type OBJECT
auto subkernel_sym = get_symbol_from_symtab(sym_index);
if (subkernel_sym.type != ELFIO::STT_OBJECT)
throw std::runtime_error("Symbol doesn't point to subkernel entry (expected STT_OBJECT)");
throw std::runtime_error(
"Symbol doesn't point to subkernel entry (expected STT_OBJECT)");

// Get parent kernel symbol - must be of type FUNC
auto kernel_sym = get_symbol_from_symtab(subkernel_sym.section_index);
if (kernel_sym.type != ELFIO::STT_FUNC)
throw std::runtime_error("Subkernel doesn't point to kernel entry (expected STT_FUNC)");
throw std::runtime_error(
"Subkernel doesn't point to kernel entry (expected STT_FUNC)");

// Demangle kernel name and extract signature
auto demangled_signature = demangle(kernel_sym.name);
Expand Down Expand Up @@ -462,14 +502,16 @@ parse_single_group_section(const ELFIO::section* section)
return;

// Parse kernel/subkernel from symtab using .group's info field
auto [kernel_name, subkernel_name] = get_kernel_subkernel_from_symtab(section->get_info());
auto [kernel_name, subkernel_name] =
get_kernel_subkernel_from_symtab(section->get_info());

// Update kernel maps
m_kernel_to_subkernels_map[kernel_name].push_back(subkernel_name);
m_kernel_name_to_id_map[kernel_name + subkernel_name] = group_id;

// Parse member section indices (skip flags at index 0)
const auto* word_data = reinterpret_cast<const ELFIO::Elf_Word*>(data);
const auto* word_data =
reinterpret_cast<const ELFIO::Elf_Word*>(data);
const auto word_count = size / sizeof(ELFIO::Elf_Word);

std::vector<uint32_t> member_sections;
Expand All @@ -482,24 +524,116 @@ parse_single_group_section(const ELFIO::section* section)
m_group_to_sections_map.emplace(group_id, std::move(member_sections));
}

// Parse .group sections in the ELF file and populate all maps
// Helper function to add custom section to kernel (instance_name is empty)
// or to an instance.
void
elf_impl::
parse_group_sections()
add_custom_section_to_kernel_or_instance(
const std::string& kernel_name,
const std::string& instance_name,
const std::string& sec_name,
detail::span<const char> data)
{
if (!is_group_elf()) {
for (auto& kernel : m_kernels) {
if (kernel.get_name() != kernel_name)
continue;

if (instance_name.empty()) {
kernel.get_handle()->add_custom_section(sec_name, data);
return;
}

for (auto& instance : kernel.get_instances()) {
if (instance.get_name() != instance_name)
continue;

instance.get_handle()->add_custom_section(sec_name, data);
return;
Comment on lines +546 to +551
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

The inner loop for (auto& instance : kernel.get_instances()) iterates over a temporary vector because kernel_impl::get_instances() returns by value. This copies the entire instance list for every custom section being attached, which can be unnecessarily expensive. Consider iterating the stored instances without copying (e.g., add a get_instances_ref() on kernel_impl, or expose a helper on kernel_impl to add a custom section by instance name).

Copilot uses AI. Check for mistakes.
}
}
}
Comment on lines +537 to +554
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

add_custom_section_to_kernel_or_instance() silently does nothing if the specified kernel/instance name is not found. That can make malformed/unsupported ELFs look like they simply have no custom sections, which is hard to debug and can lead to incorrect behavior. Consider returning a bool and having the caller throw with a clear error when the kernel/instance cannot be located (or at least log/trace the drop).

Copilot uses AI. Check for mistakes.

void
elf_impl::
parse_custom_sections(const std::vector<uint32_t>& custom_section_ids)
{
constexpr uint32_t no_symtab_idx = 0;
auto section_span = [](const ELFIO::section* sec) {
return detail::span<const char>(sec->get_data(), sec->get_size());
};

for (auto sec_id : custom_section_ids) {
auto sec = m_elfio.sections[sec_id];
if (!sec)
continue;

auto symtab_idx = sec->get_info();
auto sec_name = sec->get_name();
auto data = section_span(sec);

if (m_section_to_group_map.find(sec_id) == m_section_to_group_map.end()) {
// Section not in any group: global or kernel-level custom section.
if (symtab_idx == no_symtab_idx) {
m_global_custom_section_map[sec_name] = data;
continue;
}
// Kernel-level custom section: symtab points to kernel (STT_FUNC).
auto kernel_sym = get_symbol_from_symtab(symtab_idx);
if (kernel_sym.type != ELFIO::STT_FUNC)
throw std::runtime_error(
"custom section points to invalid symtab index (expected STT_FUNC)");
auto demangled = demangle(kernel_sym.name);
auto kernel_name = extract_kernel_name(demangled);

add_custom_section_to_kernel_or_instance(
kernel_name, "", sec_name, data);
}
else {
// Section belongs to a group: instance-level custom section.
auto [kernel_name, subkernel_name] =
get_kernel_subkernel_from_symtab(symtab_idx);

add_custom_section_to_kernel_or_instance(
kernel_name, subkernel_name, sec_name, data);
}
}
}

// Parse ELF sections and populate all maps
void
elf_impl::
parse_sections()
{
if (!is_group_elf()) { // older ELF format without .group sections
init_legacy_section_maps();
finalize_kernels();
m_kernel_args_map.clear();
return;
}

// collect custom sections and parse .group sections to populate maps
std::vector<uint32_t> custom_section_ids;
auto CUSTOM_SECTION_TYPE = ELFIO::SHT_LOUSER + 1;
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

CUSTOM_SECTION_TYPE is effectively a constant but is declared as a mutable auto variable with ALL_CAPS naming. Consider making it constexpr (and giving it an explicit ELFIO type) to document intent and avoid accidental modification.

Suggested change
auto CUSTOM_SECTION_TYPE = ELFIO::SHT_LOUSER + 1;
constexpr ELFIO::Elf_Word CUSTOM_SECTION_TYPE = ELFIO::SHT_LOUSER + 1;

Copilot uses AI. Check for mistakes.
for (const auto& section : m_elfio.sections) {
if (section && section->get_type() == ELFIO::SHT_GROUP)
if (!section)
continue;

if (section->get_type() == ELFIO::SHT_GROUP)
parse_single_group_section(section.get());
else if (section->get_type() == CUSTOM_SECTION_TYPE)
custom_section_ids.push_back(section->get_index());
}

// Build elf::kernel objects after all group sections are parsed
finalize_kernels();

// parse and collect custom sections
// This function is called after creating kernels and instances
// because custom sections are added to corresponding kernel/instance
parse_custom_sections(custom_section_ids);

// Free parsing-only data, not used after parse_sections().
m_kernel_args_map.clear();
}

// Get configuration UUID from ELF
Expand Down Expand Up @@ -594,6 +728,16 @@ get_abi_version() const
return {major, minor};
}

detail::span<const char>
elf_impl::
get_custom_section(const std::string& name)
{
if (auto it = m_global_custom_section_map.find(name); it != m_global_custom_section_map.end())
return it->second;

throw std::runtime_error("Cannot get custom section " + name + " data, section not found in ELF");
}

////////////////////////////////////////////////////////////////
// Derived class for aie2p (gen2) platform
////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -863,8 +1007,8 @@ class elf_aie_gen2 : public elf_impl
elf_aie_gen2(ELFIO::elfio&& elfio, elf::platform platform)
: elf_impl{std::move(elfio), platform}
{
// Parse group sections to populate kernel and section maps
parse_group_sections();
// Parse ELF sections to populate kernel and section maps
parse_sections();
// Initialize all section buffer maps
initialize_section_buffer_maps();
// Initialize argument patchers from relocation sections
Expand Down Expand Up @@ -1273,8 +1417,8 @@ class elf_aie_gen2_plus : public elf_impl
elf_aie_gen2_plus(ELFIO::elfio&& elfio, elf::platform platform)
: elf_impl{std::move(elfio), platform}
{
// Parse group sections to populate kernel and section maps
parse_group_sections();
// Parse ELF sections to populate kernel and section maps
parse_sections();
// Initialize all section buffer maps
initialize_section_buffer_maps();
}
Expand Down Expand Up @@ -1470,6 +1614,14 @@ get_kernels() const
return handle->get_kernels();
}

detail::span<const char>
elf::
get_custom_section(const std::string& section_name) const
{
valid_or_error(handle);
return handle->get_custom_section(section_name);
}

////////////////////////////////////////////////////////////////
// elf::kernel API implementation
////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -1502,6 +1654,13 @@ get_instances() const
return handle->get_instances();
}

detail::span<const char>
elf::kernel::
get_custom_section(const std::string& section_name) const
{
return handle->get_custom_section(section_name);
}

////////////////////////////////////////////////////////////////
// elf::kernel::instance API implementation
////////////////////////////////////////////////////////////////
Expand All @@ -1513,6 +1672,13 @@ get_name() const
return handle->get_name();
}

detail::span<const char>
elf::kernel::instance::
get_custom_section(const std::string& section_name) const
{
return handle->get_custom_section(section_name);
}

} // namespace xrt

////////////////////////////////////////////////////////////////
Expand Down
Loading
Loading