x86-64gnu-assembler

Is there a way to determine buffer's length?


Lets say I have a buffer I declared like this:

.section .bss
.lcomm buffer, 33

I pushed it to the stack and passed it as an argument to another function that expects a 44-byte length buffer.

Is there a way on the called function to check if the received buffer is 44 bytes in length? Because you cannot iterate over it and check if the current byte is equal to $0 in case the buffer is empty, because then all of its bytes will be equal to $0.


Solution

  • If you can move away from defining your buffer as "common" (.lcomm, .comm), and can just define the buffer as a normal object, I have an answer for you.

    If you must define your buffer as a common, you only know that the minimum size of the buffer is what your current assembly source file's definition uses. There might be a way to find out the common size at link time when the linker actually allocates the memory for all the different definitions of the common, but I am unaware of one.

    Anyway, back to solving this without using common objects.

    We first change our buffer definition from a "common" to a normal object:

            .section .bss
    buffer: .skip    33
    

    We can then add two lines defining the symbol buffer_end pointing to the byte just one after the end of buffer, and the symbol buffer_size as the size of the buffer in bytes:

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

    You can now use the buffer_end and buffer_size symbols in many ways like any other symbol, e.g. for passing on the buffer size or to compare an iteration pointer to buffer_end.

    You can even make the object have the proper type and size in the symbol table:

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

    which will make the buffer appear properly in the symbol list:

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

    If you want to use any of those symbols from other compilation units (e.g. other *.S or *.c source files), you can declare those symbols as global:

            .global buffer
            .global buffer_end
            .global buffer_size
    

    and then use them e.g. from assembly with

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

    or from C with

    #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]);
        }
    }