-2

hybrid.s

.section .text   # specifices the beginning of the 'text' or code section
.global collatz  # makes the name 'collatz' visible to the linker

collatz:   # int collatz(long long n) {      
     ret   # return

hybrid.c

#include <stdio.h>    
#include <stdlib.h>    
     
 
extern int collatz(long long n);    

int main(int argc, char *argv[]){    
    if (argc < 2) {    
        printf("Parameter \"n\" is missing. \n");    
        return -1;    
    }     
    int output=0;    
    long long n = atoll(argv[1]);    
    for (long long i=1 ; i<n ; i++) {    
        output = collatz(i);    
        printf("collatz(%lld) is %d\n", i,output);    
    }    
}   

I was testing what %rax is initialized to by doing

>> gcc -o hybrid hybrid.c hybrid.s

>> ./hybrid 5

Yielding:

collatz(1) is 1
collatz(2) is 2
collatz(3) is 3
collatz(4) is 4

I expected hybrid.s to always return 0, since my guess was that %rax is initialized to 0; but as you can see, this is not the case. By observing the output, my hyptothesis is that %rax is equal to %rdi by default?

This prompts the question: What is %rax initialized to?


EDIT:

In the following whenever I say "run hybrid.s isolated", I mean "run hybrid.s isolated with all occurrences of collatz replaced by '_start'", of course.

Based on @ErikEidt's comment, Shouldn't running hybrid.s isolated as follows result in just returning whatever %rax was before (last set to), since it wasn't initialized? Why do I then get a Segmentation fault (core dumped)?

>> as hybrid.s -o hybrid.o
>> ld hybrid.o hybrid
>> ./hybrid 5

Yields

Segmentation fault (core dumped)

I mean the %rax has to contain some value at any given moment .. right? So why a segmentation fault instead of just returning that value?

Sebastian Nielsen
  • 3,835
  • 5
  • 27
  • 43
  • 5
    Since `collatz` has a defined prototype and doesn't take a variable number of arguments, `rax` is undefined at the start of `collatz`. [It happens that it holds the cycle counter by chance.](https://godbolt.org/z/zhT8bv). You can see that when compiling with `-O3` it holds the result of `atoll`. The point is: `rax` is not initialized, nor to zero nor to any stable value. – Margaret Bloom Mar 20 '21 at 11:44
  • 1
    Registers hold their old values until they are repurposed. Initialization is *work*; if its going to be done, some machine code has to do it. If there's no reason to initialize something, it often isn't done. Your C code and assembly code are in disagreement about function `collatz`, so you should expect random behavior. (If you disassemble your C `main`, you'll probably see that code using `rax` (maybe in the for-loop), and if that's the case then that's where it gets it value.) – Erik Eidt Mar 20 '21 at 12:05
  • It's related to how GCC -O0 always evaluates expressions in EAX/RAX, including the `i` arg. Like in [Return value in unused parameter](https://stackoverflow.com/q/57437831), but this is happening in the caller. I thought I wrote an answer about that (where a non-void C function fell off the end, so it just returned without modifying RAX), but can't find that duplicate Q&A now. – Peter Cordes Mar 20 '21 at 12:35
  • @MargaretBloom, ErikEidt, PeterCordes , How would you then explain my edit? (Running **hybrid.s** isolated results in segmentation fault) – Sebastian Nielsen Mar 20 '21 at 12:48
  • How can you run `hybrid.s` alone using the commands shown? What does your linker set the entry point to? There is no `main` or `start` symbol in `hybrid.s`. Why does not the `ld` command complain of an undefined symbol? – Eric Postpischil Mar 20 '21 at 12:56
  • @EricPostpischil Oh sorry, of course, I changed all occurrences of "collatz in **hybrid.s** with "_start". – Sebastian Nielsen Mar 20 '21 at 13:04
  • Finally found the duplicate I was looking for, [Why does it return a random value other than the value I give to the function?](https://stackoverflow.com/a/66394904). Your edit introduces a totally separate bug, trying to `ret` from the process entry point `_start` (which isn't a function). [Nasm segmentation fault on RET in \_start](https://stackoverflow.com/q/19760002) – Peter Cordes Mar 20 '21 at 13:13
  • The answer to your title question is: RAX hold whatever garbage your caller left there. Or, in a fresh process after execve of a static executable, whatever *the kernel* left there. Linux chooses to zero all the regs (except RSP) to avoid leaking any leftover kernel values. [What is default register state when program launches (asm, linux)?](https://stackoverflow.com/q/9147455). You could see this with GDB on your `ld` output, using `starti` / `info reg` – Peter Cordes Mar 20 '21 at 13:14
  • 1
    If you want to understand the craziness you're attempting, then single step in the debugger and all will be revealed. Use assembly mode to single step, so you can watch it do each instructions and see where registers get their values, and why it crashes. – Erik Eidt Mar 20 '21 at 14:56

1 Answers1

-3

Compile with debug info and open run it in gdb. Gdb can set a breakpoint and you can list the contents of your registers :) funny man's industry am I right 8))))))

pmikkelsen
  • 40
  • 3