I'm actually writing this code as inline ASM but I feel it is more relevant to ASM than it is to C...

Basically all I want to do for now is print the alphabet. However I'm having some trouble getting used to AT&T syntax (I would rather learn it than keep just using Intel syntax) and I'm also unsure as to how to solve this segfault I'm getting:

#include <stdio.h>

int main() {
    // code
    {
        asm volatile("movb %ah, 0x4C\n" /* BIOS interrupt call for output */
            "movb $'A', %AL\n"          /* Place "A" in AL                */
            "jmp start\n"
            "start:\n\t"
                "add $1, %AL\n\t"   /* Add 1 to 'A' to move on to 'B', etc. */
                "print:\n\t\t"
                    "int $0x21\n\t" /* Print contents of AL */
              #if 0
                "hlt\n"       /* Halt the machine (commented out because I think I need ring 0 access to use hlt) */
              : " %ax", "%bx" /* List of registers used (commented out because GCC complained of the comma)       */
              #endif
            );
    }
    // code
    return 0;
}

I have tried without the "volatile" keyword. I did nothing, as I expected...

Thank you :)

Dani AI

Generated

A few concise, practical points that fill gaps left by the thread and help diagnose the segfault in ’s early attempts (and why ’s direction toward syscalls/64-bit ABI was the right move).

First, common root causes in this situation: (a) AT&T vs Intel operand conventions and size suffixes — easy to mix up; (b) attempting BIOS/DOS-style interrupts or writing to very low physical addresses from userland — those addresses are not mapped under Linux and will fault; (c) executing privileged instructions (like HLT) from ring 3 produces a CPU exception. The observable result is typically a killed process (SIG from the kernel) or an immediate segfault when the process tries to access unmapped memory or execute a privileged op.

A practical workflow to isolate the problem:

  • Build with debug info and no optimizations: gcc -g -O0 foo.c -o foo.
  • Inspect the emitted asm: gcc -S -fverbose-asm foo.c or objdump -d ./foo.
  • Run under GDB and catch the signal: gdb ./foorun ; on failure bt and x/i $rip to see the instruction that faulted ; info registers to inspect operands.
  • Check address mappings (did the code try to touch address 0x004c?): cat /proc/$(pgrep foo)/maps (or examine core dump).

Quick best-practices and fixes that weren’t fully spelled out in the thread:

  • Keep inline asm tiny and let the compiler manage registers via extended-asm inputs/outputs and clobbers. Large routines with labels and jumps belong in a separate .s file or an asm function — GCC’s inline asm can confuse optimizers when control flow is embedded.
  • For experimenting with BIOS interrupts, HLT or real-mode behavior, use an emulator (QEMU/Bochs/DOSBox) or run code in a proper kernel/module or boot sector — not from Linux userland.
  • When the goal is simply “print the alphabet” while practicing assembly, do the character math in asm but use libc (putchar/write) or a single well-formed syscall wrapper for actual output; that avoids many platform/privilege pitfalls.

Tying back to the thread: ’s move toward a syscall-based example is safest for x86_64 userland; the debugging steps above will quickly show whether the crash is caused by an illegal privileged instruction or an invalid memory access.

Recommended Answers

All 10 Replies

I'm actually writing this code as inline ASM but I feel it is more relevant to ASM than it is to C...

Basically all I want to do for now is print the alphabet. However I'm having some trouble getting used to AT&T syntax (I would rather learn it than keep just using Intel syntax) and I'm also unsure as to how to solve this segfault I'm getting:

#include <stdio.h>

int main() {
    // code
    {
        asm volatile("movb %ah, 0x4C\n" /* BIOS interrupt call for output */
            "movb $'A', %AL\n"          /* Place "A" in AL                */
            "jmp start\n"
            "start:\n\t"
                "add $1, %AL\n\t"   /* Add 1 to 'A' to move on to 'B', etc. */
                "print:\n\t\t"
                    "int $0x21\n\t" /* Print contents of AL */
              #if 0
                "hlt\n"       /* Halt the machine (commented out because I think I need ring 0 access to use hlt) */
              : " %ax", "%bx" /* List of registers used (commented out because GCC complained of the comma)       */
              #endif
            );
    }
    // code
    return 0;
}

I have tried without the "volatile" keyword. I did nothing, as I expected...

Thank you :)

Your seg fault is probably from this line

movb %ah, 0x4C\n

Your moving the contents of ah into memory address 0x4c

Are you writing this for Linux or Windows?

Your seg fault is probably from this line

movb %ah, 0x4C\n

Your moving the contents of ah into memory address 0x4c

AHHH! Drat! The first tutorial I ever read was in Intel syntax; so as I say I'm likely to make many mistakes in AT&T syntax. I think, however, that it is probably better to write AT&T -- when you learn it I think it's more explicit -- you can easily see what are variables, what is in hex, and what is being used as a register.

Thank you :)

Are you writing this for Linux or Windows?

Linux.

I just figured out why the list of registers didn't work. You're meant to put %% where you use them instead of a singe %; as per this article http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html#s5

Do you know if I need ring 0 access to use halt? I would assume so because IIRC it hangs the machine; and obviously you don't want me running code on your machine that halts execution :P

Thanks.

AHHH! Drat! The first tutorial I ever read was in Intel syntax; so as I say I'm likely to make many mistakes in AT&T syntax. I think, however, that it is probably better to write AT&T -- when you learn it I think it's more explicit -- you can easily see what are variables, what is in hex, and what is being used as a register.

Thank you :)


Linux.

I just figured out why the list of registers didn't work. You're meant to put %% where you use them instead of a singe %; as per this article http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html#s5

Do you know if I need ring 0 access to use halt? I would assume so because IIRC it hangs the machine; and obviously you don't want me running code on your machine that halts execution :P

Thanks.

If your going to be messing around with assembly system programming then you should download the Intel or AMD manuals, they have explanations for all the opcodes..

If your going to be messing around with assembly system programming

Yes, I will be, but not for a while. I just wanted to play with hlt. And by a while I mean a few years. I'm nowhere near that level yet.

you should download the Intel or AMD manuals, they have explanations for all the opcodes..

Oh, thanks :-)

I have decided to try this in a plain asm file using as and ld;

.intel_syntax noprefix

.globl _start

_start:  
    mov ah, 0x4C
    mov al, 'A'

    abc:
        nextletter:
            add ah, 1
        print:
            int 0x21

I don't understand where I'm getting a segfault.
I thought it was because of my use of AT&T syntax and not knowing it properly; so I tried with intel syntax and it didn't help :l

Does anyone know where the segfault is?

Thanks :l

If this is for linux, why are you using int 21. shouldn't you be using

movl	$4, %eax
movl	$1, %ebx
movl	pointer to whatever you want to print, %ecx
movl	len of what you want to print, %edx
int $0x80

Try this code...I'm not sure if your running a 32 or 64 bit box...

#include <stdio.h>
#include <stdlib.h>

void printit(char *chr)
{
	//64 bit
	__asm__ __volatile__
	(
	 	"movq	$1, %%rax\n\t"
		"movq	$1, %%rdi\n\t"	
		"movq	%0, %%rsi\n\t"
		"movq	$1, %%rdx\n\t"
		"syscall\n\t"
		:"=m"(chr)
	);
	//32 bit - Note untested
	//__asm__ __volatile__
	//(
	//	"movl	$4, %%eax\n\t"
	//       	"movl	$1, %%ebx\n\t"
	//	"movl	%0, %%ecx\n\t"
	//	"movl	$1, %%edx\n\t"
	//	"int	$0x80\n\t"
	//	:"=m"(chr)	
	//);
}

int main(int argc, char**argv)
{
	char ch = 0x61;
	int i = 0;

	for (i = 0; i < 26; ++i)
	{
		printit(&ch);
		++ch;
	}
	exit(EXIT_SUCCESS);
}

Try this code...I'm not sure if your running a 32 or 64 bit box...

#include <stdio.h>
#include <stdlib.h>

void printit(char *chr)
{
	//64 bit
	__asm__ __volatile__
	(
	 	"movq	$1, %%rax\n\t"
		"movq	$1, %%rdi\n\t"	
		"movq	%0, %%rsi\n\t"
		"movq	$1, %%rdx\n\t"
		"syscall\n\t"
		:"=m"(chr)
	);
	//32 bit - Note untested
	//__asm__ __volatile__
	//(
	//	"movl	$4, %%eax\n\t"
	//       	"movl	$1, %%ebx\n\t"
	//	"movl	%0, %%ecx\n\t"
	//	"movl	$1, %%edx\n\t"
	//	"int	$0x80\n\t"
	//	:"=m"(chr)	
	//);
}

int main(int argc, char**argv)
{
	char ch = 0x61;
	int i = 0;

	for (i = 0; i < 26; ++i)
	{
		printit(&ch);
		++ch;
	}
	exit(EXIT_SUCCESS);
}

I'm using x86_64 so yes, 64-bit.
And thanks :) that does work.
I'll play around with that now.

I almost forgot to tell you, if your using 64 bit linux assembly programming then most of the books and tuts out there are out dated...The ABI(application binary interface) for Linux 64 bit programs are different than the 32 bit ABI...Below is an excerpt from the Entry_64.S file from a 64 bit kernel source...Please note the order that registers are pasted data for a system call...You'll see I used this order in the 64 bit code above

%rax
%rdi
%rsi
%rdx
%r10
%r8
%r9
%r11

/*
 * System call entry. Upto 6 arguments in registers are supported.
 *
 * SYSCALL does not save anything on the stack and does not change the
 * stack pointer.
 */

/*
 * Register setup:
 * rax  system call number
 * rdi  arg0
 * rcx  return address for syscall/sysret, C arg3
 * rsi  arg1
 * rdx  arg2
 * r10  arg3 	(--> moved to rcx for C)
 * r8   arg4
 * r9   arg5
 * r11  eflags for syscall/sysret, temporary for C
 * r12-r15,rbp,rbx saved by C code, not touched.
 *
 * Interrupts are off on entry.
 * Only called from user space.
 *
 * XXX	if we had a free scratch register we could save the RSP into the stack frame
 *      and report it properly in ps. Unfortunately we haven't.
 *
 * When user can change the frames always force IRET. That is because
 * it deals with uncanonical addresses better. SYSRET has trouble
 * with them due to bugs in both AMD and Intel CPUs.
 */

I almost forgot to tell you, if your using 64 bit linux assembly programming then most of the books and tuts out there are out dated...The ABI(application binary interface) for Linux 64 bit programs are different than the 32 bit ABI...Below is an excerpt from the Entry_64.S file from a 64 bit kernel source...Please note the order that registers are pasted data for a system call...You'll see I used this order in the 64 bit code above

%rax
%rdi
%rsi
%rdx
%r10
%r8
%r9
%r11

/*
 * System call entry. Upto 6 arguments in registers are supported.
 *
 * SYSCALL does not save anything on the stack and does not change the
 * stack pointer.
 */

/*
 * Register setup:
 * rax  system call number
 * rdi  arg0
 * rcx  return address for syscall/sysret, C arg3
 * rsi  arg1
 * rdx  arg2
 * r10  arg3 	(--> moved to rcx for C)
 * r8   arg4
 * r9   arg5
 * r11  eflags for syscall/sysret, temporary for C
 * r12-r15,rbp,rbx saved by C code, not touched.
 *
 * Interrupts are off on entry.
 * Only called from user space.
 *
 * XXX	if we had a free scratch register we could save the RSP into the stack frame
 *      and report it properly in ps. Unfortunately we haven't.
 *
 * When user can change the frames always force IRET. That is because
 * it deals with uncanonical addresses better. SYSRET has trouble
 * with them due to bugs in both AMD and Intel CPUs.
 */

I'll bear that in mind, thanks :)

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.