Skip to content

Add changes to get custom section data from xrt::elf#9664

Open
rbramand-xilinx wants to merge 1 commit intoXilinx:masterfrom
rbramand-xilinx:custom_section
Open

Add changes to get custom section data from xrt::elf#9664
rbramand-xilinx wants to merge 1 commit intoXilinx:masterfrom
rbramand-xilinx:custom_section

Conversation

@rbramand-xilinx
Copy link
Collaborator

Problem solved by the commit

Added changes as per spec - https://amd.atlassian.net/wiki/spaces/AIE/pages/1341720313/Spec+of+xrt+elf+APIs to get custom section data from xrt::elf object.

Custom section can be global level (not associated with any kernel/compute unit), kernel level and instance level. Added apis in respective classes to get the data.

Bug / issue (if any) fixed, which PR introduced the bug, how it was discovered

It is a new feature

How problem was solved, alternative solutions (if any) and why they were rejected

Added public API get_custom_section in xrt::elf class to get global custom section data, in xrt::elf::kernel class to get kernel custom section data and in xrt::elf::kernel::instance class to get instance custom section data

The return type for all these API's is xrt::detail::span. Ideally it should be std::span but as XRT uses c++17 and std::span is available from c++20, we are using a lightweight span class implemented in XRT. Move to std::span when XRT moves to c++ 20

Risks (if any) associated the changes in the commit

Low

What has been tested and how, request additional testing if necessary

Tested using full ELF with custom sections and retrieving both text and binary custom section data works as expected

Documentation impact (if any)

Added doxygen comments wherever needed.

Example showing how to retrieve custom section data :
1. Global-level custom section
Custom sections that are not tied to any kernel or instance are read from the xrt::elf object:

#include "xrt/experimental/xrt_elf.h"
#include <fstream>

xrt::elf elf("test.elf");

// Read global custom section with section name "Global_config"
auto all_data = elf.get_custom_section("Global_config");

// Use the data (e.g. write to file)
std::ofstream out("global_config.bin", std::ios::binary);
out.write(all_data.data(), static_cast<std::streamsize>(all_data.size()));

2. Kernel-level custom section
Sections attached to a kernel (not to a specific instance) are read from the corresponding xrt::elf::kernel:

xrt::elf elf("test.elf");
auto kernels = elf.get_kernels();

// read custom section from kernel **DPU** 
for (const auto& kernel : kernels) {
  if (kernel.get_name() != "DPU")
    continue;

  // Read kernel-level custom section (e.g. "METADATA", "CONFIG")
  auto metadata = kernel.get_custom_section("METADATA");

  std::string out_name = std::string("DPU_") + "METADATA.bin";
  std::ofstream out(out_name, std::ios::binary);
  out.write(metadata.data(), static_cast<std::streamsize>(metadata.size()));
  break;
}

3. Instance-level custom section
Sections attached to a specific kernel instance are read from the corresponding xrt::elf::kernel::instance:

xrt::elf elf("my_application.elf");
auto kernels = elf.get_kernels();

for (const auto& kernel : kernels) {
  if (kernel.get_name() != "RESNET")
    continue;

  for (const auto& instance : kernel.get_instances()) {
    // Read instance-level custom section
    auto config = instance.get_custom_section("CONFIG");

    // e.g. filename: kernel_name + "_" + instance_name + "_config"
    std::string out_name = kernel.get_name() + "_" + instance.get_name() + "_config.bin";
    std::ofstream out(out_name, std::ios::binary);
    out.write(config.data(), static_cast<std::streamsize>(config.size()));
  }
  break;
}

Signed-off-by: rahul <rbramand@amd.com>
@github-actions
Copy link
Contributor

clang-tidy review says "All clean, LGTM! 👍"

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds public APIs to retrieve custom ELF section data from xrt::elf at global, kernel, and kernel-instance scope, and wires up parsing/storage of those custom sections in the ELF runtime implementation.

Changes:

  • Added get_custom_section() APIs on xrt::elf, xrt::elf::kernel, and xrt::elf::kernel::instance returning xrt::detail::span<const char>.
  • Implemented parsing of custom sections (SHT_LOUSER + 1) and routing of section data into global/kernel/instance maps.
  • Refactored ELF parsing entry point from .group-only parsing to a unified parse_sections() flow.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 9 comments.

File Description
src/runtime_src/core/include/xrt/experimental/xrt_elf.h Exposes new public APIs (and doxygen) for retrieving custom section data at multiple scopes.
src/runtime_src/core/common/api/xrt_elf.cpp Implements storage and lookup for custom sections, plus parsing changes to collect and classify custom sections.
src/runtime_src/core/common/api/elf_int.h Adds internal storage for global custom sections and declares new parsing helpers / accessor.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

// 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.
Comment on lines +328 to +330
// 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;
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.
Comment on lines +537 to +554
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;
}
}
}
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.
Comment on lines +546 to +551
for (auto& instance : kernel.get_instances()) {
if (instance.get_name() != instance_name)
continue;

instance.get_handle()->add_custom_section(sec_name, data);
return;
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.

// 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.
Comment on lines +315 to +330
/**
* get_custom_section() - Get Global custom section data from ELF
*
* @param section_name
* Name of the custom section
*
* @return
* A span representing the custom section data
*
* @note
* Returns xrt::detail::span (lightweight span) for now. Will switch to
* std::span when XRT uses C++20 by default.
*/
XRT_API_EXPORT
xrt::detail::span<const char>
get_custom_section(const std::string& section_name) const;
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 new APIs return a non-owning xrt::detail::span into ELF section storage, but the doxygen comment doesn’t specify the required lifetime (e.g., whether the returned view is only valid while the originating xrt::elf object remains alive). Please document the lifetime/ownership guarantees explicitly so callers don’t accidentally keep and use the span after the backing ELF data is destroyed.

Copilot uses AI. Check for mistakes.
Comment on lines +78 to +93
/**
* get_custom_section() - Get custom section data of an instance from ELF
*
* @param section_name
* Name of the custom section
*
* @return
* A span representing the custom section data
*
* @note
* Returns xrt::detail::span (lightweight span) for now. Will switch to
* std::span when XRT uses C++20 by default.
*/
XRT_API_EXPORT
xrt::detail::span<const char>
get_custom_section(const std::string& section_name) const;
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::instance::get_custom_section() returns a non-owning span, but the comment doesn’t state how long the returned view remains valid (what object owns the backing bytes). Please document the lifetime/ownership guarantees explicitly to prevent callers from using a span after the backing ELF storage has been released.

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is a valid point. At least state that the span is valid only as long as the xrt::elf is in scope.

Comment on lines +152 to +167
/**
* get_custom_section() - Get custom section data of a kernel from ELF
*
* @param section_name
* Name of the custom section
*
* @return
* A span representing the custom section data
*
* @note
* Returns xrt::detail::span (lightweight span) for now. Will switch to
* std::span when XRT uses C++20 by default.
*/
XRT_API_EXPORT
xrt::detail::span<const char>
get_custom_section(const std::string& section_name) const;
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::get_custom_section() returns a non-owning span but the doxygen does not describe lifetime/ownership of the returned data. Please add an explicit lifetime note (e.g., valid while the originating ELF object and its backing storage remain alive) so callers know whether they must copy the data before storing it.

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Ditto, yes.

Comment on lines +247 to +250
// 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;

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants