1

I have written a primitive version of malloc in x86 assembler as an exercise. The code uses a linked list to keep track of allocated memory blocks. I decided to add a function to walk the list and print out the meta data for each block and encountered this weird problem. When I run the code using gdb it works properly but when run directly without gdb it does not. When I print out an address returned by sbrk as a hex string it only prints correctly if run from gdb. If run repeatedly without gdb it prints a different number each run. I have cut the code down to the minimum needed to illustrate the problem. I have tried everything I can think of to find the problem. I'm sure that my itoh and printstring funcions are working correctly. I have tried linking with the c library and using puts but it does the same. I tried initializing all registers to zero. I have looked for any registers altered by the call to sbrk and saved and restored them across the call. Nothing has worked. Here is the code that illustrates the problem:

global _start,itoh,printstring

section .rodata
        TRUE            equ 1
        FALSE           equ 0
        NULL            equ 0
        LF              equ 10
        sys_brk         equ 12
        exit_ok         equ 0
        sys_exit        equ 60
        sys_write       equ 1
        stdout          equ 1
        

section .data
        current_brk     dq 1
        linefeed        db LF, NULL
        msg1            db 'Test should print 0x403000 from constant: ', NULL
        msg2            db 'Test should print 0x403000 from sys_brk return: ', NULL
        number          db '--------------------', NULL

section .text

_start: mov rdi, msg1
        call printstring
        mov rdi, 0x403000
        mov rsi, number 
        mov rdx, TRUE
        call itoh
        mov rdi, number 
        call printstring

        mov rax, sys_brk
        syscall
        mov [current_brk], rax
        mov rdi, msg2
        call printstring
        mov rdi, [current_brk]
        mov rsi, number 
        mov rdx, TRUE
        call itoh
        mov rdi, number 
        call printstring

.exit:  mov rax, sys_exit
        mov rdi, exit_ok
        syscall
;
; itoh  - rdi intger to convert
;       - rsi address of string to return result
;       - rdx if true add a newline to string
;       return nothing
itoh:   push rcx
        push rax
        xor r10, r10        ; r10 counts the digits pushed onto stack
        mov r9, rdx         ; save newline flag in r9
        mov rax, rdi        ; rax is bottom half of dividend
        mov rcx, 16         ; rcx is divisor

.div:   xor rdx, rdx        ; zero rdx, top half of 128 bit dividend
        div rcx             ; divide rdx:rax by rcx
        push rdx            ; rdx is remainder
        inc r10             ; increment digit counter
        cmp rax, 0          ; is quotient zero?
        jne .div            ; no - keep dividimg by 16 and pushing remainder

.pop:   mov byte[rsi], "0"
        inc rsi
        mov byte[rsi], "x"
        inc rsi
.p0:    pop r11             ; get a digit from stack
        cmp r11, 10
        jl .p1
        sub r11, 10
        add r11, "a"
        jmp .p2
.p1:    add r11, "0"        ; convert to ascii char
.p2:    mov byte[rsi],r11b  ; copy ascii digit to string buffer
        dec r10             ; decrement digit count
        inc rsi             ; point rsi to next char position
        cmp r10, 0          ; is digit counter 0
        jne .p0             ; no, go get another digit from stack
        cmp r9, 0
        je .exit
        mov byte[rsi], LF
        inc rsi

.exit:  mov byte[rsi], NULL ; terminate string
        pop rax
        pop rcx
        ret
;
; printstring - rdi is address of string
;               return nothing
printstring:
        push rcx            ; sys_write modifies rcx
        push rax            ; sys_write modifies rax
        xor rdx, rdx        ; zero rdx, char count
        mov rsi, rdi        ; use rsi to index into string
.countloop:
        cmp byte [rsi],NULL ; end of string?
        je .countdone       ; yes, finished counting
        inc rdx             ; no, count++
        inc rsi             ; point to next char
        jmp .countloop
.countdone:
        cmp rdx, 0          ; were there any characters?
        je .printdone       ; no - exit

        mov rax, sys_write  ; write system call
        mov rsi, rdi        ; address of string
        mov rdi, stdout     ; write to stdout
        syscall             ; number of bytes to write is in rdx
.printdone:
        pop rax
        pop rcx
        ret

yasm -felf64 -gdwarf2 test.asm
ld -g -otest test.o

gdb test
Type "apropos word" to search for commands related to "word"...
Reading symbols from test...
[?2004h(gdb) run
[?2004l
Starting program: /home/david/asm/test 
Test should print 0x403000 from constant: 0x403000
Test should print 0x403000 from sys_brk return: 0x403000
[Inferior 1 (process 28325) exited normally]
[?2004h[?2004l
[?2004h(gdb) q
[?2004l

./test
Test should print 0x403000 from constant: 0x403000
Test should print 0x403000 from sys_brk return: 0x14cf000
  • 1
    GDB disables ASLR by default; maybe try with `set disable-randomization off` to run with random stack addresses. It may also randomize the break, even in your non-PIE executable. (I assume this is non-PIE, otherwise `0x403000` wouldn't be remotely near a valid address.) Yeah, looks like you're actually printing out the values to confirm that. So probably your program is running fine, but the address simply isn't the same as inside GDB. – Peter Cordes Mar 08 '22 at 05:08
  • Thanks Peter, that explains it. I hadn't considered that addresses might be randomized. I am using yasm and I believe non-PIE is the default. I haven't done any assembler programming since working on a PDP-10 40 years ago so I have a lot to learn. Thanks again for your timely assistance. – David Whitsed Mar 08 '22 at 07:50
  • non-PIE is the `ld` default yes, but not for `gcc -nostdlib`. It's a linker option; the necessary code differences are up to you when writing by hand in asm (avoiding 32-bit absolute addresses). – Peter Cordes Mar 08 '22 at 09:06

0 Answers0