1

Now the code is here:

.code32
#PURPOSE - Given a number, this program computes the
# factorial. For example, the factorial of
# 3 is 3 * 2 * 1, or 6. The factorial of
# 4 is 4 * 3 * 2 * 1, or 24, and so on.
#
.section .data
#This program has no global data
.section .text
.globl _start
.globl factorial #this is unneeded unless we want to share
_start:
pushl $4 
call factorial
addl $4, %esp
#the stack
movl %eax, %ebx 
#status

movl $1, %eax
int $0x80

.type factorial,@function
factorial:
pushl %ebp
movl %esp, %ebp 
movl 8(%ebp), %eax

cmpl $1, %eax 
je end_factorial
decl %eax 
pushl %eax 
call factorial 
movl 8(%ebp), %ebx 
imull %ebx, %eax
end_factorial:
movl %ebp, %esp
popl %ebp 
ret 

This code is in Programming from the Ground Up I need help to run this code right in WSL2. Well,as the Title,why this ASM-code demo throw segmentation fault in x86/64-Linux machine? I'm new in ASM-code. Thanks from China.

  • 2
    It works correctly for me on Ubuntu 22.04, and returns exit code 24. How are you building the program? I used `gcc -m32 -nostdlib -o fac fac.s`. I wonder if you have the wrong build options and are making a 64-bit executable by mistake. – Nate Eldredge Jul 31 '22 at 15:05
  • Last time I checked, WSL does not support 32 bit code. That may have changed though. – fuz Jul 31 '22 at 15:13
  • @fuz, WSL doesn't support running 32-bit, WSL2 does. – Michael Petch Jul 31 '22 at 17:34
  • 1
    OP, do not supply a `.code32` directive! This directive is not needed for correct code and masks you incorrect build flags. – fuz Jul 31 '22 at 19:29

1 Answers1

3

In your assembly code when you call factorial you pass the argument using the stack with pushl $4. That is 32-bit assembly and needs to be linked as a 32-bit executable. In 64-bit the parameters are passed in registers, which would be %rdi in this case (first parameter).

A 64-bit x86 cpu can run 32-bit code but the binary needs to flag that. Compile your code with gcc -m32 test.s -nostdlib -o test then debug it with gdb:

gdb ./test.1
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
...
Reading symbols from ./test.1...
(No debugging symbols found in ./test.1)
(gdb) b _start
Breakpoint 1 at 0x1000
(gdb) r
Starting program: /tmp/test.1 

Breakpoint 1, 0x56556000 in _start ()
(gdb) set disassemble-next-line on
(gdb) si
0x56556014 in factorial ()
=> 0x56556014 <factorial+1>:    89 e5   mov    %esp,%ebp
(gdb) 
0x56556016 in factorial ()
=> 0x56556016 <factorial+3>:    8b 45 08        mov    0x8(%ebp),%eax
(gdb) 
0x56556019 in factorial ()
=> 0x56556019 <factorial+6>:    83 f8 01        cmp    $0x1,%eax
(gdb) 
0x5655601c in factorial ()
=> 0x5655601c <factorial+9>:    74 0d   je     0x5655602b <end_factorial>
(gdb) 

Something Something
  • 3,999
  • 1
  • 6
  • 21
  • The code won't assemble in 64 bit mode anyway; `pushl %ebp` is not a valid instruction in 64 bit mode. – fuz Jul 31 '22 at 17:39
  • @fuz Aha unfortunately it will. At least on Ubuntu 20.04/gcc 9.4 – Something Something Jul 31 '22 at 18:22
  • 3
    Ah, I totally missed the `.code32` directive. Seems like OP found that it won't assemble and so slapped this directive on, “fixing” the assembly. But now what he really has is 32 bit code in a 64 bit binary which won't work at all. Folks, the `.code32` directive will not solve your problems! Do not use it unless you know exactly what you are doing! – fuz Jul 31 '22 at 18:24
  • 1
    If the program had any static data (in `.data` or `.bss`), you'd also want to use `-static` or `-no-pie` on distros where the GCC default is `-pie`. `mov $foo, %eax` needs an absolute address in the machine code, but a PIE executable doesn't know the right absolute address until it runs. In a dynamically-linked PIE (not static-pie), the dynamic linker can apply those runtime fixups, but it's probably easier to debug and compare with objdump output if absolute addresses simpler, not 0x5655... And GDB's `starti` to stop before the first user-space insn will be in ld.so, not your `_start`. – Peter Cordes Aug 01 '22 at 21:15
  • 1
    Also, your answer should mention that `.code32` is not helpful, and is only useful if you want to put 32-bit machine code in a 64-bit `.o`, e.g. if you're making a kernel that changes modes. [Assembling 32-bit binaries on a 64-bit system (GNU toolchain)](https://stackoverflow.com/q/36861903) is a duplicate of this which points that out. – Peter Cordes Aug 01 '22 at 21:17