Есть ли способ определить длину буфера?

Допустим, у меня есть буфер, который я объявил следующим образом:

.section .bss
.lcomm buffer, 33

Я поместил его в стек и передал в качестве аргумента другой функции, которая ожидает буфер длиной 44 байта.

Есть ли способ вызываемой функции проверить, имеет ли длина полученного буфера 44 байта? Потому что вы не можете перебирать его и проверять, равен ли текущий байт $0, если буфер пуст, потому что тогда все его байты будут равны $0.

Если вы передаете адрес буфера функции, то единственная передаваемая информация — это адрес буфера — где может храниться что-то еще? Если вы хотите проверить длину, вам придется либо передать ее как отдельный параметр, либо потребовать, чтобы длина сохранялась в начале буфера (а затем сохранялась после длины).

jasonharper 27.04.2024 23:20
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
87
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Если вы можете отказаться от определения своего буфера как «общего» (.lcomm, .comm) и просто определить буфер как обычный объект, у меня есть для вас ответ.

Если вам необходимо определить свой буфер как общий, вы знаете только, что минимальный размер буфера — это тот, который использует определение текущего исходного файла сборки. Возможно, существует способ узнать общий размер во время компоновки, когда компоновщик фактически выделяет память для всех различных определений общего, но я об этом не знаю.

В любом случае, вернемся к решению этой проблемы без использования общих объектов.

Сначала мы меняем определение нашего буфера с «общего» на обычный объект:

        .section .bss
buffer: .skip    33

Затем мы можем добавить две строки, определяющие символ buffer_end, указывающий на байт сразу после конца buffer, и символ buffer_size как размер buffer в байтах:

        .section .bss
buffer: .skip    33
buffer_end:
        .set     buffer_size, buffer_end - buffer

Теперь вы можете использовать символы buffer_end и buffer_size разными способами, как и любой другой символ, например. для передачи размера буфера или для сравнения указателя итерации с buffer_end.

  • Вы можете увидеть значение символа в списке символов, например. с nm или objdump --syms:

    0000000000000021 l       *ABS*  0000000000000000     buffer_size
    0000000000404078 l       .bss   0000000000000000     buffer
    0000000000404099 l       .bss   0000000000000000     buffer_end
    

    Обратите внимание, что размер 0000000000000000 здесь по-прежнему неправильный, мы исправим это позже.

  • Вы можете использовать эти символы из кода C или ассемблерного кода для проверок во время выполнения, добавляя параметр размера буфера к каждой функции, которая принимает параметр указателя буфера. Это прямой ответ на ваш вопрос, если вам действительно не нужен «общий» объект.

  • Вы можете использовать символ buffer_size для проверки времени компиляции внутри исходного файла сборки, добавив

    .if buffer_size != 44
    .error "buffer_size value is incorrect"
    .endif
    
  • Вы можете использовать символ buffer_size для проверки времени компоновки с помощью скрипта компоновщика, например. -T check-buffer-size.x или -Wl,-T,check-buffer-size.x:

    /* check-buffer-size.x - link time check the buffer_size value */
    SECTIONS {
    }
    INSERT AFTER .bss ;
    ASSERT( (buffer_size == 44), "buffer_size is not the required 44" );
    

    Это дает вам утверждение времени компоновки, которое, хотя часто и полезно, кажется настолько необычной идеей, что языки программирования, такие как C, дают вам только выбор между утверждениями времени компиляции и утверждениями времени выполнения, но не утверждениями времени компоновки.

Вы даже можете указать объекту правильный тип и размер в таблице символов:

        .section .bss
        .type    buffer, @object
buffer: .skip    33
buffer_end:
        .set     buffer_size, buffer_end - buffer
        .size    buffer, buffer_size

благодаря чему буфер будет правильно отображаться в списке символов:

0000000000000021 l       *ABS*  0000000000000000    buffer_size
0000000000404078 l       .bss   0000000000000021    buffer
0000000000404099 l       .bss   0000000000000000    buffer_end

Если вы хотите использовать любой из этих символов из других модулей компиляции (например, других исходных файлов *.S или *.c), вы можете объявить эти символы как глобальные:

        .global buffer
        .global buffer_end
        .global buffer_size

а затем использовать их, например. от сборки с

        movq    buffer, %rdi
loop:   /* do_something with memory at (%rdi) */
        add     %4, %rdi
        cmp     %rdi, buffer_end
        jne     loop

или из C с помощью

#include <stdint.h>
extern uint32_t buffer[];
extern uint32_t buffer_end[];
extern char buffer_size[];
extern const uintptr_t buffer_size_in_bytes = (const uintptr_t) &buffer_size;

void foo(void) {
    for (uint32_t *p=buffer; p<buffer_end; ++p) {
        handle_buffer_element(*p);
    }
}

void bar(void) {
    const uintptr_t buffer_size_in_elements = buffer_size_in_bytes / 4;
    for (uintptr_t i=0; i<buffer_size_in_elements; ++i) {
        handle_buffer_element(buffer[i]);
    }
}

Большое спасибо за ваш ответ! это здорово, я многому научился благодаря этому!

user16713791 06.05.2024 15:34

@user16713791 user16713791 Мне пришлось полностью переписать ответ, чтобы избавиться от «общего» типа объекта (.comm или .lcomm), потому что «общие» объекты размещаются компоновщиком, и очень сложно определить метки для начала и конца " общие» объекты, созданные компоновщиком. Мне следовало протестировать и убедиться, что мое необычное решение также работает, прежде чем публиковать его. Извините, что вызвал путаницу. Мне кажется, что отказ от использования «common» — единственный способ, но у кого-то с большим опытом сборки все равно может быть решение с «common» объектами.

ndim 08.05.2024 01:31

Другие вопросы по теме