-6

I've been working on this program all night. I happened to see a Youtube video about self-changing code and said it was a bad idea, so I decided I'd try doing it myself, in assembly of course. After a few hours, I actually got it working, albeit with hard-coded numbers. It's nothing extravagant, just a proof of concept. It takes two values, now called val1 and val2, adds them, and prints the result to the screen. However, if val1 is greater than val2, it actually changes the opcode for add to sub. Again, I had it working with the values hard-coded and just recompiling every time I wanted to change them.

But while I'm not going all out on this project, I would at least like to get the numbers from the user so it's easy to see it works by typing different numbers instead of having to recompile. I got the printf function working no problem, but try as I may, I couldn't get scanf to work at all. It segfaulted every time I got to it. I considered that maybe it's because there are many different forms of scanf depending on the parameters and the parameters aren't so clearly defined in assembly as in C, so I changed my methodology a bit. I decided to make a separate C routine for getting the two values, and maybe more later to make printing them less messy.

At this point, I've made the function and it can see val1 and val2, it compiles, my function gets called from the main routine composed of assembly, but just as before, it segfaults at scanf. After about six hours of working on this and missing a lot of sleep, I'm finally throwing in the towel for tonight. But of course first I have to show you what I have. Here is the simple C routine I made to get the numbers.

#include <stdio.h>

extern long int val1;
extern long int val2;

void get_values() {
    printf("Enter two integers:\n");
    scanf("%ld", &val1);
    scanf("%ld", &val2);
    return;
}

If that's not enough, here's the head of my assembly file. I'll show the whole thing if I can get this working and there's interest.

.global main
.global val1
.global val2

.bss
    .lcomm val1, 8
    .lcomm val2, 8

(And an example of how I called scanf from the assembly routine)

.text

movq $scanf_identifier_string, %edi
movq $val1, %esi
call scanf

I feel like this should be so simple, yet it has me completely buffaloed. I was really excited that I managed to make a program that overwrites its own instructions dynamically, but a bit embarrassed that I can't get scanf to work. I appreciate any help with this.

Kenshi
  • 1
  • 1
    Why was this tagged java? – possum Aug 21 '20 at 14:40
  • https://godbolt.org/z/W9vh7G – Erik Eidt Aug 21 '20 at 18:17
  • 1
    You didn’t provide nearly enough context. This code won’t even assemble, because you use movq with a dword register. – prl Aug 21 '20 at 18:19
  • Two problems: you don’t set al to 0 and you probably don’t align the stack to a multiple of 16. – prl Aug 21 '20 at 18:20
  • @mangusta, you don’t push arguments in x86-64 (unless there are more than 6). – prl Aug 21 '20 at 18:21
  • Does this answer your question? [NASM x86\_64 scanf segmentation fault](https://stackoverflow.com/questions/26889692/nasm-x86-64-scanf-segmentation-fault) – prl Aug 21 '20 at 18:24
  • I suggest using LEA with an RIP-relative addressing mode to load addresses in 64-bit mode, like this: `lea val1(%rip), %rsi`. – prl Aug 21 '20 at 18:28
  • It's my first time posting to this site and it's frustrating the hell out of me, so bear with me. I apparently spent too long (more than 10 minutes) editing my last post because I didn't see the last couple of replies so I'm gonna have to post it again. And this was why it was tagged as Java; I tried more appropriate tags but it said they weren't in the top 200 or something and wouldn't let me post. Forgive my hastiness, but I didn't feel like spending another hour just posting here and instead getting 3 hours of sleep before work. – Kenshi Aug 21 '20 at 18:40
  • prl - Okay, I messed up on the assembly part. I had already erased it since I made the C routine, but it did have rdi and rsi. I also did an xor %rax, %rax, since I do that before printf as well though I can't remember why. I'd been awake for about 20 hours, so it was likely I'd mess that up somehow. It did assemble, and the header part was copied and pasted. Your link that talks about aligning the stack and providing the number of parameters in rdi is probably spot on though. I'll have to try that after work. Any idea what's wrong with the C function though? And why does printf work as is? – Kenshi Aug 21 '20 at 18:41
  • If your code was the only code in the `.text` section it would successfully call `scanf` (because the stack is aligned, and AL will even be zero), if you used `mov` instead of `movq` and built a static binary. And then it would fall off the end of `_start` if you don't call exit or make an exit system call. Otherwise you're probably getting stack alignment wrong. [glibc scanf Segmentation faults when called from a function that doesn't align RSP](https://stackoverflow.com/q/51070716). But your [mcve] is too minimal and you haven't showed where it's faulting (use a debugger) so we can't tell. – Peter Cordes Aug 21 '20 at 19:28
  • 1
    Although the x86-64 ABI requires the stack to be 16-byte aligned before any function call, in my experience scanf is more likely to fail than printf when you fail to do it. That doesn’t make it any less wrong for printf, though. – prl Aug 22 '20 at 05:23

1 Answers1

0

Okay, I solved it. It was the stack alignment that was causing the issues, nothing more. I figured that out over an hour ago but wanted to finish my program so I could post it here. Keep in mind I didn't spend time making sure scanf returns valid results and optimizing it every way I could. My intention wasn't to make this so novices could use it nor is it my most beautiful programming ever. I just wanted to see if I could change the machine code in my program and I was successful. With that in mind, you can make suggestions as to how I could improve my programming if you like if there are obvious and simple ways it could've been done better.

It asks for two numbers and has an instruction to add them, but just before that, it checks to see if the first number is greater, and if it is, it changes the add opcode to a sub opcode as well as changes the + to a - in the printf string. The one catch is that I had to define a special writeable text section. I use gcc to assemble it.

.global main

.data

request:
    .string "Enter two integers:\n"

scanf_string:
    .string "%ld"

result:
    .string "%d + %d = %d\n"

opcode:
    .string "Opcode: %#x\n"

.bss
    .lcomm val1, 8
    .lcomm val2, 8

.section text, "awx"

main:
    // Align stack
    subq $8, %rsp

    // Get our two numbers from user
    // (No error checking for now)

    xorq %rax, %rax
    movq $request, %rdi
    call printf
    
    movq $scanf_string, %rdi
    movq $val1, %rsi
    call scanf

    movq $scanf_string, %rdi
    movq $val2, %rsi
    call scanf

    // Load both variables into registers
    // and check which is greater

    movq (val1), %rsi
    movq (val2), %rdx
    cmp %rdx, %rsi
    jl preinstruction # First number is less, so we add

    // If first is greater or equal, change
    // operation to subtraction

    movq $instruction+1, %rbx
    movb (%rbx), %r8b
    xorb $0x28, %r8b # Changes add opcode to sub opcode
    movb %r8b, (%rbx)

    // Change + to - in result string
    movq $result+3, %rbx
    movb $45, %r8b # 45 is ASCII value for -
    movb %r8b, (%rbx)

    // Add (or subtract) values and print

    preinstruction:
    movq %rsi, %rcx

    instruction:
    addq %rdx, %rcx
    xorq %rax, %rax
    movq $result, %rdi
    call printf

    movq $opcode, %rdi
    movq $instruction, %rbx
    incq %rbx
    xor %rsi, %rsi
    movb (%rbx), %sil
    xorq %rax, %rax
    call printf

    add $8, %rsp
    xorq %rax, %rax
    ret
Kenshi
  • 1
  • By the way, my answer earlier may have come across as if I figured the issue out by myself. I meant I figured out which potential problem was the actual issue and finished the program. I want to thank those who helped me figure out the stack needed to be aligned or helped in any way. – Kenshi Aug 22 '20 at 10:00