Unable to use .data variables

I’m getting errors when I try to use variables declared in the .data section and I’m not sure how to fix it.

My code (shortened):

section .data
you: db "you"

section .bss
output: resb 255

section .text
global two_fer
two_fer:
    mov rdi, you
    ret

%ifidn __OUTPUT_FORMAT__,elf64
section .note.GNU-stack noalloc noexec nowrite progbits
%endif

Error:

/usr/lib/gcc/x86_64-alpine-linux-musl/12.2.1/../../../../x86_64-alpine-linux-musl/bin/ld: two_fer.o: warning: relocation in read-only section `.text'
/usr/lib/gcc/x86_64-alpine-linux-musl/12.2.1/../../../../x86_64-alpine-linux-musl/bin/ld: warning: creating DT_TEXTREL in a PIE
collect2: error: ld returned 1 exit status
make: *** [Makefile:32: tests] Error 1

I also get the below error with my full code. default rel doesn’t fix it.

/usr/lib/gcc/x86_64-alpine-linux-musl/12.2.1/../../../../x86_64-alpine-linux-musl/bin/ld: two_fer.o: relocation R_X86_64_32S against `.bss' can not be used when making a PIE object; recompile with -fPIE
/usr/lib/gcc/x86_64-alpine-linux-musl/12.2.1/../../../../x86_64-alpine-linux-musl/bin/ld: failed to set dynamic section sizes: bad value
collect2: error: ld returned 1 exit status
make: *** [Makefile:32: tests] Error 1

Look at the solution for Hello World.

It starts with

default rel

and the most important instruction used is lea (load effective address).

I did not use section .bss in my Two Fer solution. We are given an output pointer to write to.

I had already tried this a few minutes after posting this. No dice. Also, removing the .bss section seems to have no effect.

Here’s the full code:

default rel

section .data
oneFor: db "One for "
oneForLength: equ $ - oneFor
you: db "you"
youLength: equ $ - you
oneForMe: db ", one for me.", 0
oneForMeLength: equ $ - oneForMe

section .bss
output: resb 255

section .text
global two_fer
two_fer:
    ; Provide your implementation here
    mov rbx, rdi
    
    cld
    ; Put oneFor in the output
    lea rsi, [oneFor]
    lea rdi, [output]
    mov rcx, oneForLength
    rep movsb
    
    mov rax, [rbx]
    test rax, rax
    jnz copy
    
    ; Put you in the output
    lea rsi, [you]
    lea rdi, [output + oneForLength]
    mov rcx, youLength
    rep movsb
    mov rcx, youLength
    jmp complete
    
    ; Put input in the output
copy:
    mov al, byte [rbx+rcx]
    test al, al
    jz complete
    mov byte [output + oneForLength + rcx], al
    inc rcx
    jmp copy
    
complete: ; Complete the output with oneForMe
    lea rsi, [oneForMe]
    lea rdi, [output + oneForLength + rcx]
    mov rcx, oneForMeLength
    rep movsb
    lea rax, [output]
    ret

%ifidn __OUTPUT_FORMAT__,elf64
section .note.GNU-stack noalloc noexec nowrite progbits
%endif
/usr/lib/gcc/x86_64-alpine-linux-musl/12.2.1/../../../../x86_64-alpine-linux-musl/bin/ld: two_fer.o: relocation R_X86_64_32S against `.bss' can not be used when making a PIE object; recompile with -fPIE
/usr/lib/gcc/x86_64-alpine-linux-musl/12.2.1/../../../../x86_64-alpine-linux-musl/bin/ld: failed to set dynamic section sizes: bad value
collect2: error: ld returned 1 exit status
make: *** [Makefile:32: tests] Error 1

I coded this locally on my PC before moving it to Exercism editor.

I’m not aware of this. What output pointer were we given?

Exercism uses the System V Application Binary Interface, see Useful x86-64 Assembly resources.

From section 3.2.1

Registers %rbp, %rbx and %r12 through %r15 “belong” to the calling function and the called function is required to preserve their values. In other words, a called function must preserve these registers’ values for its caller.

It is common for functions that use rbx to have push rbx at the start of the function, and pop rbx just before the return.

From section 3.2.3

Passing Once arguments are classified, the registers get assigned (in left-to-right order) for passing as follows:

  1. If the class is MEMORY, pass the argument on the stack.
  2. If the class is INTEGER, the next available register of the sequence %rdi, %rsi, %rdx, %rcx, %r8 and %r9 is used

If we look at the Two Fer tests, we have

extern void two_fer(const char *name, char *buffer);

So the read-only string name is being passed in rdi, and the buffer for the output string is being passed in rsi.

Early in your code, you might have something like

mov r8, rdi ; name
mov rdi, rsi ; buffer

Much appreciated.