root/src/system/runtime_loader/commpage.cpp
/*
 * Copyright 2025, Trung Nguyen, trungnt282910@gmail.com.
 * Distributed under the terms of the MIT License.
 */
#include "commpage.h"

#include <string.h>

#include <syscalls.h>

#include "runtime_loader_private.h"


static const char* const kCommpageName = "commpage";


static image_id sCommpageImageID = -1;
static int32 sCommpageSymbolCount = -1;
static void* sCommpageSymbolsBuffer = NULL;
static elf_sym* sCommpageSymbols = NULL;
static char* sCommpageStringTable = NULL;


status_t
commpage_reinit_after_fork()
{
        sCommpageImageID = -1;
        return B_OK;
}


static status_t
get_commpage_image_id(team_id team)
{
        image_info info;
        int32 cookie = 0;
        while (_kern_get_next_image_info(team, &cookie, &info, sizeof(info)) == B_OK) {
                if (strcmp(info.name, kCommpageName) == 0)
                        return info.id;
        }
        return B_ENTRY_NOT_FOUND;
}


status_t
get_nearest_commpage_symbol_at_address_locked(void* address, image_id* _imageID, char** _imagePath,
        char** _imageName, char** _symbolName, int32* _type, void** _location, bool* _exactMatch)
{
        status_t status;
        bool wantsImage = _imageID != NULL;
        bool wantsSymbol = _symbolName != NULL || _type != NULL || _location != NULL;

        if (wantsImage && sCommpageImageID == -1) {
                if (sCommpageImageID == -1) {
                        status = get_commpage_image_id(B_CURRENT_TEAM);
                        if (status < B_OK)
                                return status;
                        sCommpageImageID = status;
                }
        }

        if (wantsSymbol && sCommpageSymbolCount == -1) {
                image_id systemCommpageID = get_commpage_image_id(B_SYSTEM_TEAM);
                if (systemCommpageID < B_OK)
                        return systemCommpageID;

                int32 symbolCount = 0;
                size_t stringTableSize = 0;

                status = _kern_read_kernel_image_symbols(systemCommpageID, NULL,
                        &symbolCount, NULL, &stringTableSize, NULL);
                if (status < B_OK)
                        return status;

                size_t memorySize = symbolCount * sizeof(elf_sym) + stringTableSize + 1;
                void* buffer = malloc(memorySize);
                if (buffer == NULL)
                        return B_NO_MEMORY;

                elf_sym* symbols = (elf_sym*)buffer;
                char* stringTable = (char*)buffer + symbolCount * sizeof(elf_sym);
                status = _kern_read_kernel_image_symbols(systemCommpageID, symbols, &symbolCount,
                        stringTable, &stringTableSize, NULL);
                if (status < B_OK) {
                        free(buffer);
                        return status;
                }

                sCommpageSymbolCount = symbolCount;
                sCommpageSymbolsBuffer = buffer;
                sCommpageSymbols = symbols;
                sCommpageStringTable = stringTable;
        }

        if (_imageID != NULL)
                *_imageID = sCommpageImageID;
        if (_imagePath != NULL)
                *_imagePath = (char*)kCommpageName;
        if (_imageName != NULL)
                *_imageName = (char*)kCommpageName;

        bool exactMatch = false;
        elf_sym* foundSymbol = NULL;
        addr_t foundLocation = (addr_t)NULL;

        for (int32 i = 0; i < sCommpageSymbolCount && !exactMatch; i++) {
                elf_sym* symbol = &sCommpageSymbols[i];
                addr_t location = (addr_t)__gCommPageAddress + symbol->st_value;

                if (location <= (addr_t)address && location >= foundLocation) {
                        foundSymbol = symbol;
                        foundLocation = location;

                        // jump out if we have an exact match
                        if (location + symbol->st_size > (addr_t)address) {
                                exactMatch = true;
                                break;
                        }
                }
        }

        if (_exactMatch != NULL)
                *_exactMatch = exactMatch;

        if (foundSymbol != NULL) {
                *_symbolName = sCommpageStringTable + foundSymbol->st_name;

                if (_type != NULL) {
                        if (foundSymbol->Type() == STT_FUNC)
                                *_type = B_SYMBOL_TYPE_TEXT;
                        else if (foundSymbol->Type() == STT_OBJECT)
                                *_type = B_SYMBOL_TYPE_DATA;
                        else
                                *_type = B_SYMBOL_TYPE_ANY;
                }

                if (_location != NULL)
                        *_location = (void*)foundLocation;
        } else {
                *_symbolName = NULL;
                if (_location != NULL)
                        *_location = NULL;
        }

        return B_OK;
}