Skip to content

src: add GetBuildId helper function#339

Closed
santigimeno wants to merge 1 commit intonsolid-ebpf-devfrom
santi/add_build_id
Closed

src: add GetBuildId helper function#339
santigimeno wants to merge 1 commit intonsolid-ebpf-devfrom
santi/add_build_id

Conversation

@santigimeno
Copy link
Copy Markdown
Member

@santigimeno santigimeno commented Jul 8, 2025

To allow us to read the Build-Id from ELF headers in a specific binary.

Summary by CodeRabbit

  • New Features
    • Added a native addon method to retrieve the GNU build ID from ELF binaries on Linux.
  • Tests
    • Introduced a test script to verify that the native addon correctly extracts the build ID, comparing its output to the system's readelf command.
  • Chores
    • Updated build configuration to always link with the ELF library on Linux and conditionally exclude the real-time library.

@santigimeno santigimeno requested a review from RafaelGSS July 8, 2025 13:51
@santigimeno santigimeno self-assigned this Jul 8, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jul 8, 2025

Walkthrough

The changes introduce ELF build ID extraction utilities for Linux, including new C++ source and header files, a Node.js native addon, and a test script. Build configuration files are updated to include the new sources and adjust Linux-specific linking logic. The test verifies the new functionality by comparing extracted build IDs with system values.

Changes

File(s) Change Summary
node.gyp Added conditional inclusion of new ELF utility sources for Linux builds.
node.gypi Modified Linux linking logic: always adds -lelf, conditionally removes -lrt based on a variable.
src/nsolid/nsolid_elf_utils.cc
src/nsolid/nsolid_elf_utils.h
Introduced new ELF utility implementation and header, providing GetBuildId function for Linux.
test/addons/nsolid-elf-utils/binding.cc Added native addon exposing getBuildId(path) method to JavaScript.
test/addons/nsolid-elf-utils/binding.gyp Added build configuration for the new native addon.
test/addons/nsolid-elf-utils/nsolid-elf-utils.js Added test script to validate ELF build ID extraction on Linux.

Sequence Diagram(s)

sequenceDiagram
    participant JS as JavaScript Test
    participant Addon as Native Addon (binding.cc)
    participant Utils as ELF Utils (GetBuildId)
    participant System as Linux System

    JS->>Addon: getBuildId(path)
    Addon->>Utils: GetBuildId(path, &build_id)
    Utils->>System: Open ELF file, parse sections
    System-->>Utils: ELF data (build ID or error)
    Utils-->>Addon: build_id or error code
    Addon-->>JS: build_id string or undefined
Loading

Poem

In Linux lands where binaries dwell,
A rabbit found a build ID to tell.
With C++ and Node in a clever dance,
ELF secrets revealed at every chance.
Linking libraries, tests that sing—
Hopping forward, what will next spring bring?
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

To allow us to read the Build-Id from ELF headers in a specific binary.
@santigimeno santigimeno force-pushed the santi/add_build_id branch from b554e8a to ab9a12f Compare July 8, 2025 14:32
@santigimeno
Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jul 8, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (3)
test/addons/nsolid-elf-utils/nsolid-elf-utils.js (1)

1-1: Remove redundant use strict directive.

The 'use strict'; directive is redundant in JavaScript modules as they are automatically in strict mode.

-'use strict';
src/nsolid/nsolid_elf_utils.cc (2)

32-34: Consider using more specific error handling for elf_version.

The function returns elf_errno() but this might not provide meaningful error information to callers.

   if (elf_version(EV_CURRENT) == EV_NONE) {
-    return elf_errno();
+    return UV_ENOSYS;  // System function not implemented
   }

66-73: Improve note parsing robustness.

The note parsing logic looks correct but could benefit from additional validation.

         uint32_t* note = reinterpret_cast<uint32_t*>(data->d_buf);
         uint32_t namesz = note[0];
         uint32_t descsz = note[1];
+        uint32_t type = note[2];
+        
+        // Verify this is actually a build-id note (NT_GNU_BUILD_ID = 3)
+        if (type != 3) {
+          continue;
+        }
+        
         // Name starts at offset 12
         // Descriptor (build-id) starts at next aligned offset
         size_t name_end = 12 + ((namesz + 3) & ~3);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b7798c3 and ab9a12f.

📒 Files selected for processing (7)
  • node.gyp (1 hunks)
  • node.gypi (1 hunks)
  • src/nsolid/nsolid_elf_utils.cc (1 hunks)
  • src/nsolid/nsolid_elf_utils.h (1 hunks)
  • test/addons/nsolid-elf-utils/binding.cc (1 hunks)
  • test/addons/nsolid-elf-utils/binding.gyp (1 hunks)
  • test/addons/nsolid-elf-utils/nsolid-elf-utils.js (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
test/addons/nsolid-elf-utils/nsolid-elf-utils.js (1)
Learnt from: santigimeno
PR: nodesource/nsolid#327
File: test/addons/nsolid-code-event-hook/binding.cc:64-78
Timestamp: 2025-06-10T09:52:49.401Z
Learning: In the nsolid codebase, test files in test/addons/ consistently use assert() for input validation and runtime checks, not CHECK() macros. This is the established testing convention.
🧬 Code Graph Analysis (1)
test/addons/nsolid-elf-utils/binding.cc (3)
test/addons/nsolid-elf-utils/nsolid-elf-utils.js (1)
  • assert (3-3)
src/nsolid/nsolid_elf_utils.h (1)
  • GetBuildId (12-12)
src/nsolid/nsolid_elf_utils.cc (2)
  • GetBuildId (17-92)
  • GetBuildId (17-17)
🪛 Biome (1.9.4)
test/addons/nsolid-elf-utils/nsolid-elf-utils.js

[error] 1-1: Redundant use strict directive.

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.
Safe fix: Remove the redundant use strict directive.

(lint/suspicious/noRedundantUseStrict)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: coverage-linux-without-intl
  • GitHub Check: test-linux (ubuntu-24.04-arm)
  • GitHub Check: test-linux (ubuntu-24.04)
  • GitHub Check: test-macOS
  • GitHub Check: build-tarball
  • GitHub Check: coverage-linux
  • GitHub Check: lint-js-and-md
  • GitHub Check: coverage-windows
  • GitHub Check: build-docs
🔇 Additional comments (9)
test/addons/nsolid-elf-utils/binding.gyp (1)

1-10: LGTM - Standard addon build configuration

The build configuration is well-structured and appropriate for a native addon that needs access to internal Node.js APIs.

node.gyp (1)

576-581: LGTM - Proper Linux-specific integration

The conditional addition of ELF utility sources for Linux builds is correctly implemented and follows the existing patterns in the build configuration.

src/nsolid/nsolid_elf_utils.h (1)

1-20: LGTM - Well-structured header file

The header file follows Node.js conventions with proper include guards, namespace structure, and conditional compilation for internal APIs.

node.gypi (1)

553-560: LGTM - Proper linking configuration for ELF support

The changes correctly ensure -lelf is linked on Linux builds while maintaining the conditional -lrt removal logic. This supports the new ELF utility functionality.

test/addons/nsolid-elf-utils/nsolid-elf-utils.js (2)

7-11: LGTM: Proper platform check for Linux-only functionality.

The platform check correctly ensures the test only runs on Linux, which is appropriate for ELF utilities.


20-23: LGTM: Test logic is correct and comprehensive.

The test correctly compares the addon's output with the expected value from readelf, with a helpful error message showing both values.

src/nsolid/nsolid_elf_utils.cc (3)

1-11: LGTM: Proper includes and dependencies.

The includes are appropriate for ELF processing, with both low-level POSIX headers and libelf-specific headers.


36-39: LGTM: Proper file opening with error handling.

The file opening logic correctly handles errors and returns appropriate errno values.


85-91: LGTM: Proper resource cleanup with goto pattern.

The cleanup logic correctly handles both ELF and file descriptor resources using the goto pattern, which is appropriate for C-style error handling.

static void GetBuildId(const FunctionCallbackInfo<Value>& args) {
#if defined(__linux__)
Isolate* isolate = args.GetIsolate();
assert(args[0]->IsString());
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add missing include for assert

The code uses assert() without including the necessary header.

+#include <cassert>
 #include <node.h>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
assert(args[0]->IsString());
#include <cassert>
#include <node.h>
🤖 Prompt for AI Agents
In test/addons/nsolid-elf-utils/binding.cc at line 16, the code uses assert()
but does not include the required header file. Add the appropriate include
directive for the assert header, typically #include <cassert>, at the top of the
file to ensure assert() is properly declared.

Comment thread test/addons/nsolid-elf-utils/binding.cc
Comment thread test/addons/nsolid-elf-utils/nsolid-elf-utils.js
Comment thread test/addons/nsolid-elf-utils/nsolid-elf-utils.js
Comment on lines +79 to +83
if (build_id->empty()) {
ret = UV_ENOENT;
} else {
build_id_cache_[path] = *build_id;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix thread safety issue with cache update.

The cache update outside the mutex protection (from the earlier thread safety fix) will cause race conditions.

If implementing the mutex fix suggested earlier, ensure the cache update is also protected:

   if (build_id->empty()) {
     ret = UV_ENOENT;
   } else {
+    std::lock_guard<std::mutex> lock(cache_mutex_);
     build_id_cache_[path] = *build_id;
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (build_id->empty()) {
ret = UV_ENOENT;
} else {
build_id_cache_[path] = *build_id;
}
if (build_id->empty()) {
ret = UV_ENOENT;
} else {
std::lock_guard<std::mutex> lock(cache_mutex_);
build_id_cache_[path] = *build_id;
}
🤖 Prompt for AI Agents
In src/nsolid/nsolid_elf_utils.cc around lines 79 to 83, the update to
build_id_cache_ is done outside the mutex lock, causing potential race
conditions. To fix this, move the cache update inside the mutex protection block
so that both the check and the update to build_id_cache_ happen atomically under
the same mutex lock.

Comment thread src/nsolid/nsolid_elf_utils.cc
char* name = elf_strptr(e, shstrndx, shdr.sh_name);
if (name && strcmp(name, ".note.gnu.build-id") == 0) {
Elf_Data* data = elf_getdata(scn, nullptr);
if (data && data->d_size >= 16) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add bounds checking for note data size.

The current check data->d_size >= 16 is insufficient. The actual required size depends on the note header fields.

-      if (data && data->d_size >= 16) {
+      if (data && data->d_size >= 12) {  // Minimum for note header
         // ELF Note header: namesz(4), descsz(4), type(4) + name padding
         // Compute offset to build-id properly
         uint32_t* note = reinterpret_cast<uint32_t*>(data->d_buf);
         uint32_t namesz = note[0];
         uint32_t descsz = note[1];
+        
+        // Verify we have enough data for the complete note
+        size_t name_end = 12 + ((namesz + 3) & ~3);
+        if (data->d_size < name_end + descsz) {
+          continue;  // Skip malformed note
+        }
+        
-        // Name starts at offset 12
-        // Descriptor (build-id) starts at next aligned offset
-        size_t name_end = 12 + ((namesz + 3) & ~3);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (data && data->d_size >= 16) {
if (data && data->d_size >= 12) { // Minimum for note header
// ELF Note header: namesz(4), descsz(4), type(4) + name padding
// Compute offset to build-id properly
uint32_t* note = reinterpret_cast<uint32_t*>(data->d_buf);
uint32_t namesz = note[0];
uint32_t descsz = note[1];
// Verify we have enough data for the complete note
size_t name_end = 12 + ((namesz + 3) & ~3);
if (data->d_size < name_end + descsz) {
continue; // Skip malformed note
}
// ... rest of processing for a well-formed note ...
}
🤖 Prompt for AI Agents
In src/nsolid/nsolid_elf_utils.cc at line 63, the current check for note data
size using `data->d_size >= 16` is too simplistic. Update the condition to
calculate the required size based on the note header fields (such as namesz,
descsz, and header size) and ensure `data->d_size` is at least this computed
size before proceeding. This will prevent out-of-bounds access by validating the
actual expected size of the note data.

@santigimeno
Copy link
Copy Markdown
Member Author

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.

2 participants