I was trying to create an empty window using X11 and managed to do so with the following C:
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
Display *display;
Window window;
XEvent event;
int screen;
int main(void) {
display = XOpenDisplay(NULL);
if (display == NULL) {
perror("Cannot open display\n");
exit(1);
}
screen = DefaultScreen(display);
window = XCreateSimpleWindow(display, RootWindow(display, screen), 10, 10, 500, 500, 1, BlackPixel(display, screen), WhitePixel(display, screen));
// Interested in the delete window message
Atom wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False);
XSetWMProtocols(display, window, &wm_delete_window, 1);
// Select events
XSelectInput(display, window, ExposureMask | KeyPressMask);
// Show window
XMapWindow(display, window);
while (1) {
XNextEvent(display, &event);
if (event.type == ClientMessage && event.xclient.data.l[0] == wm_delete_window) {
break;
}
}
// Cleanup
XCloseDisplay(display);
return 0;
}
However, I would like to learn assembly and how to interface it with C, so I started to recreate it line-by-line:
global _start
;
; EXTERNAL SYMBOLS (X11)
;
extern XOpenDisplay
;
; CONSTANTS
;
SYS_WRITE equ 1
SYS_EXIT equ 60
STDOUT equ 1
;
; DATA - Initialized data
;
SECTION .data
msg db "Hello, World!", 10
msg_len equ $-msg
;
; BSS - Uninitialized data
;
section .bss
display resq 1 ; Reserve space for a 64-bit pointer
window resq 1 ; Reserve space for a 64-bit pointer
event resb 24 ; Reserve space for an XEvent, which is typically 24 bytes
screen resd 1 ; Reserve space for a 32-bit integer
;
; TEXT - Code
;
SECTION .text
_start:
xor rdi, rdi ; NULL
call XOpenDisplay
mov [display], rax ; return value in RAX
; Hello world, because why not
mov rax, SYS_WRITE
mov rdi, STDOUT
mov rsi, msg
mov rdx, msg_len
syscall
; Exit
mov rax, SYS_EXIT
mov rdi, 0
syscall
I compile both projects with the same Makefile
using run_c
and run_asm
, respectively:
C_TARGET := c
ASM_TARGET := asm
BUILD_DIR := build
CC := gcc
ASM := nasm
LD := ld
LDFLAGS := -lX11
DEBUGFLAGS = -g
RELEASEFLAGS = -O3
.PHONY: run_asm run_c clean
$(BUILD_DIR)/$(ASM_TARGET): $(BUILD_DIR)/$(ASM_TARGET).o
$(LD) -o $@ $< $(LDFLAGS)
$(BUILD_DIR)/$(ASM_TARGET).o: main.asm | $(BUILD_DIR)
$(ASM) -f elf64 $< -o $@ $(DEBUGFLAGS)
$(BUILD_DIR)/$(C_TARGET): main.c | $(BUILD_DIR)
$(CC) -o $@ $< $(LDFLAGS) $(DEBUGFLAGS)
$(BUILD_DIR):
mkdir -p $@
run_asm: $(BUILD_DIR)/$(ASM_TARGET)
$(BUILD_DIR)/$(ASM_TARGET)
run_c: $(BUILD_DIR)/$(C_TARGET)
$(BUILD_DIR)/$(C_TARGET)
clean:
rm -rf $(BUILD_DIR)
The C compiles, links, and runs, but the assembly doesn't. It compiles and links, but when I attempt to run it using make run_asm
, I get the following error:
make: build/asm: No such file or directory
The binary is in the correct place, and it doesn't work when I run it directly from the command line. I thought it might be a file permissions problem, so I added this line to the $(BUILD_DIR)/$(ASM_TARGET)
rule:
chmod +x $@
This did nothing, as the file has -rwxr-xr-x
permissions with or without the chmod
. I'm new to nasm
and assembly and would greatly appreciate any help.