64-bit version.

This commit is contained in:
PoroCYon 2019-01-31 00:14:36 +01:00 committed by PoroCYon
parent cccd9656de
commit 38ea8e9531
14 changed files with 458 additions and 55 deletions

View File

@ -4,6 +4,8 @@ SRCDIR := src
LDDIR := ld
TESTDIR:= test
BITS ?= $(shell getconf LONG_BIT)
# -mpreferred-stack-boundary=3 messes up the stack and kills SSE!
COPTFLAGS=-Os -fvisibility=hidden -fwhole-program \
-ffast-math -funsafe-math-optimizations -fno-stack-protector -fomit-frame-pointer \
@ -15,12 +17,18 @@ CXXOPTFLAGS=$(COPTFLAGS) \
CFLAGS=-Wall -Wextra -Wpedantic -std=gnu11 -nostartfiles -fno-PIC $(COPTFLAGS)
CXXFLAGS=-Wall -Wextra -Wpedantic -std=c++11 $(CXXOPTFLAGS) -nostartfiles -fno-PIC
ASFLAGS=-f elf -I $(SRCDIR)/
ASFLAGS=-I $(SRCDIR)/
ifeq ($(BITS),32)
LDFLAGS=-m elf_i386
ASFLAGS += -f elf32
else
LDFLAGS=-m elf_x86_64
ASFLAGS += -f elf64
endif
LDFLAGS_=$(LDFLAGS) -T $(LDDIR)/link.ld --oformat=binary
CFLAGS += -m32
CXXFLAGS += -m32
CFLAGS += -m$(BITS)
CXXFLAGS += -m$(BITS)
LIBS=-lc
@ -29,7 +37,7 @@ ASFLAGS += -DUSE_INTERP
NASM ?= nasm
PYTHON3 ?= python3
all: $(BINDIR)/sdl $(BINDIR)/hello
all: $(BINDIR)/hello $(BINDIR)/sdl
LIBS += -lSDL2 -lGL
@ -42,25 +50,22 @@ clean:
.SECONDARY:
$(OBJDIR)/%.o: $(SRCDIR)/%.c $(OBJDIR)/
$(CC) -m32 $(CFLAGS) -c "$<" -o "$@"
$(CC) $(CFLAGS) -c "$<" -o "$@"
$(OBJDIR)/%.o: $(TESTDIR)/%.c $(OBJDIR)/
$(CC) -m32 $(CFLAGS) -c "$<" -o "$@"
$(CC) $(CFLAGS) -c "$<" -o "$@"
$(OBJDIR)/%.start.o: $(OBJDIR)/%.o $(OBJDIR)/crt1.o
$(LD) $(LDFLAGS) -r -o "$@" $^
$(OBJDIR)/crt1.o: $(SRCDIR)/crt1.c $(OBJDIR)/
$(CC) $(CFLAGS) -c "$<" -o "$@"
$(OBJDIR)/symbols.%.asm: $(OBJDIR)/%.start.o
$(PYTHON3) ./smol.py $(LIBS) "$<" "$@"
$(OBJDIR)/stub.%.o: $(OBJDIR)/symbols.%.asm $(SRCDIR)/header.asm \
$(SRCDIR)/loader.asm
$(OBJDIR)/stub.%.o: $(OBJDIR)/symbols.%.asm $(SRCDIR)/header32.asm \
$(SRCDIR)/loader32.asm
$(NASM) $(ASFLAGS) $< -o $@
$(BINDIR)/%: $(OBJDIR)/%.start.o $(OBJDIR)/stub.%.o $(BINDIR)/
$(LD) $(LDFLAGS_) $(OBJDIR)/$*.start.o $(OBJDIR)/stub.$*.o -o "$@"
$(LD) -Map=$(BINDIR)/$*.map $(LDFLAGS_) $(OBJDIR)/$*.start.o $(OBJDIR)/stub.$*.o -o "$@"
.PHONY: all clean

View File

@ -2,25 +2,50 @@ OUTPUT_FORMAT(binary)
SECTIONS {
. = 0x400000;
_smol_origin = .;
.header : { *(.header) }
_smol_text_start = .;
_smol_text_off = _smol_text_start - _smol_origin;
.text : {
*(.text.startup.smol)
*(.text.startup._start)
*(.text .text.* .rdata .rdata.* .rodata .rodata.*)
}
_smol_text_end = .;
_smol_text_size = _smol_text_end - _smol_text_start;
_smol_textandheader_size = _smol_text_end - _smol_origin;
_smol_data_start = .;
_smol_data_off = _smol_data_start - _smol_origin;
.data : {
*(.data .data.* .tdata .tdata.* .bss .bss.* .tbss .tbss.*)
*(.data.smolgot)
*(.data .data.* .tdata .tdata.*)
}
.dynamic : { *(.dynamic) } :all :dyn
.dynstuff : { *(.symtab .strtab .shstrtab .rel.text .got.plt .gnu.linkonce.* .plt .plt.got .interp) } :all
_smol_data_end = .;
_smol_data_size = _smol_data_end - _smol_data_start;
_smol_total_filesize = . - _smol_origin;
_smol_bss_start = .;
_smol_bss_off = _smol_bss_start - _smol_origin;
.bss : {
*(.bss .bss.* .tbss .tbss.* .sbss .sbss.*)
}
_smol_bss_end = .;
_smol_bss_size = _smol_bss_end - _smol_bss_start;
_smol_dataandbss_size = _smol_bss_end - _smol_data_start;
/DISCARD/ : {
*(.*)
}
_smol_total_size = . - 0x400000;
_smol_total_memsize = . - _smol_origin;
}

View File

@ -11,14 +11,16 @@ def hash_djb2(s):
def output_x86(libraries, outf):
outf.write('; vim: set ft=nasm:\n') # be friendly
outf.write('bits 32\n')
shorts = { l: l.split('.', 1)[0].lower().replace('-', '_') for l in libraries }
outf.write('%include "header.asm"\n')
outf.write('%include "header32.asm"\n')
outf.write('.dynamic.needed:\n')
for library in libraries:
outf.write('dd 1\n')
outf.write('dd 1;DT_NEEDED\n')
outf.write('dd (_symbols.{} - _symbols)\n'.format(shorts[library]))
outf.write('.dynamic.end:\n')
outf.write('_symbols:\n')
for library, symbols in libraries.items():
outf.write('\t_symbols.{}: db "{}",0\n'.format(shorts[library], library))
@ -31,15 +33,53 @@ def output_x86(libraries, outf):
\t\t dd 0x{hash:x}
""".format(name=sym, hash=hash).lstrip('\n'))
outf.write('\tdb 0\n')
outf.write('\tdb 0\n') # TODO: not a dd?
outf.write('db 0\n')
outf.write('%include "loader.asm"\n')
outf.write('_symbols.end:\n')
outf.write('%include "loader32.asm"\n')
def output_amd64(libraries, outf):
outf.write('; vim: set ft=nasm:\n')
outf.write('bits 64\n')
shorts = { l: l.split('.', 1)[0].lower().replace('-', '_') for l in libraries }
outf.write('%include "header64.asm"\n')
outf.write('dynamic.needed:\n')
for library in libraries:
outf.write('dq 1;DT_NEEDED\n')
outf.write('dq (_symbols.{} - _symbols)\n'.format(shorts[library]))
outf.write('dynamic.end:\n')
outf.write('[section .data.smolgot]\n')
outf.write('_symbols:\n')
for library, symbols in libraries.items():
outf.write('\t_symbols.{}: db "{}",0\n'.format(shorts[library], library))
for sym in symbols:
hash = hash_djb2(sym)
outf.write('\t\t_symbols.{lib}.{name}: dq 0x{hash:x}\n'\
.format(lib=shorts[library],name=sym,hash=hash))
outf.write('\tdq 0\n')
outf.write('db 0\n')
outf.write('_symbols.end:\n')
outf.write('_smolplt:\n')
for library, symbols in libraries.items():
for sym in symbols:
outf.write("""
[section .text.smolplt.{name}]
global {name}
{name}:
jmp [rel _symbols.{lib}.{name}]
""".format(lib=shorts[library],name=sym).lstrip('\n'))
outf.write('_smolplt.end:\n')
outf.write('%include "loader64.asm"\n')
def output(arch, libraries, outf):
if arch == 'i386': output_x86(libraries, outf)
##elif arch == 'arm':
#elif arch == 'x86_64':
###elif arch == 'aarch64':
elif arch == 'x86_64': output_amd64(libraries, outf)
else:
eprintf("E: cannot emit for arch '" + str(arch) + "'")
sys.exit(1)

View File

@ -2,10 +2,8 @@
import sys
archmagic = {
'i386': 3, 3: 'i386',
##'arm': 40, 40: 'arm',
#'x86_64': 62, 62: 'x86_64',
###'aarch64': 183, 183: 'aarch64'
'i386': 3, 3: 'i386' ,
'x86_64': 62, 62: 'x86_64',
}
def eprintf(*args, **kwargs): print(*args, file=sys.stderr, **kwargs)

View File

@ -8,10 +8,12 @@ extern int __libc_start_main(int (*main)(int, char**),
void (*rtld_fini)(void),
void* stack) __attribute__((__noreturn__));
__attribute__((__externally_visible__, __section__(".text.startup._start"), __noreturn__))
__attribute__((__externally_visible__, __section__(".text.startup._start"),
__noreturn__))
int _start(void* stack) {
int argc=*(int*)stack;
char** argv=(void*)(&((int*)stack)[1]);
// TODO: _dl_fini etc.
int argc=*(size_t*)stack;
char** argv=(void*)(&((size_t*)stack)[1]);
__libc_start_main(main, argc, argv, NULL, NULL, NULL, (void*)stack);

View File

@ -1,6 +1,10 @@
; vim: set ft=nasm:
%if __BITS__ == 32
%define EI_CLASS (1) ; 1 == 32-bit
%else
%define EI_CLASS (2) ; 2 == 64-bit
%endif
%define EI_DATA (1) ; 1 == little-endian
%define EI_VERSION (1) ; current
%define EI_OSABI (3) ; Linux
@ -9,7 +13,11 @@
%define ELF_TYPE (2) ; 2 == executable
%ifndef ELF_MACHINE
%define ELF_MACHINE (3) ; 3 == i386
%if __BITS__ == 32
%define ELF_MACHINE ( 3) ; 3 == i386
%else
%define ELF_MACHINE (62) ; 62 == x86_64
%endif
%endif
%define PT_LOAD (1)

View File

@ -1,10 +1,7 @@
; vim: set ft=nasm:
%define ORIGIN 0x400000
;org ORIGIN
bits 32
%include "linkscr.inc"
extern _smol_total_size
[section .header]
%include "elf.inc"
@ -25,7 +22,33 @@ header:
dd 0 ; e_flags
dw (.segments - header) ; e_ehsize
dw (.segments.load - .segments.dynamic) ; e_phentsize
%ifdef USE_NX
%error "USE_NX not supported yet on i386 ('GOT' still needs RWX, and alignment has to be fixed)"
;%ifdef USE_INTERP
; dw 4, 0 ; e_phnum, e_shentsize
;%else
; dw 3, 0
;%endif
;.segments:
;.segments.load.text:
; dd PT_LOAD
; dd _smol_origin
; dd _smol_text_start, 0
; dd _smol_textandheader_size
; dd _smol_textandheader_size
; dd (PHDR_R | PHDR_X)
; dd 0x1000
;.segments.load.data:
; dd PT_LOAD
; dd _smol_data_off
; dd _smol_data_start, 0
; dd _smol_data_size
; dd _smol_dataandbss_size
; dd (PHDR_R | PHDR_W)
; dd 0x1;000
%else
.segments:
%endif
%ifdef USE_INTERP
.segments.interp:
dd PT_INTERP ; {e_phnum: 2, e_shentsize: 0}, p_type
@ -42,14 +65,17 @@ header:
dd (.dynamic.end - .dynamic) ; p_filesz
dd (.dynamic.end - .dynamic) ; p_memsz
dd 0, 0 ; p_flags, p_align
%ifndef USE_NX
.segments.load:
dd PT_LOAD ; p_type: 1 = PT_LOAD
dd 0 ; p_offset
dd ORIGIN, 0 ; p_vaddr, p_paddr
dd _smol_total_size ; p_filesz
dd _smol_total_size ; p_memsz
dd _smol_origin, 0 ; p_vaddr, p_paddr
; use memsize twice here, linux doesn't care and it compresses better
dd _smol_total_memsize ; p_filesz
dd _smol_total_memsize ; p_memsz
dd (PHDR_R | PHDR_W | PHDR_X) ; p_flags
dd 0x1000 ; p_align
%endif
.segments.end:
%ifdef USE_INTERP
.interp:

84
src/header64.asm Normal file
View File

@ -0,0 +1,84 @@
; vim: set ft=nasm:
%include "linkscr.inc"
[section .header]
%include "elf.inc"
ehdr:
; e_ident
db 0x7F, "ELF"
db EI_CLASS, EI_DATA, EI_VERSION, EI_OSABI
db EI_OSABIVERSION
times 7 db 0
dw ELF_TYPE ; e_type
dw ELF_MACHINE ; e_machine
dd EI_VERSION ; e_version
dq _smol_start ; e_entry
dq phdr - ehdr ; e_phoff
dq 0 ; e_shoff
dd 0 ; e_flags
dw ehdr.end - ehdr ; e_ehsize
dw phdr.load - phdr.dynamic ; e_phentsize
%ifdef USE_NX
%ifdef USE_INTERP
dw 4 ; e_phnum
%else
dw 3 ; e_phnum
%endif
dw 0, 0, 0 ; e_shentsize, e_shnum, e_shstrndx
%else
phdr:
%ifdef USE_INTERP
phdr.interp:
dd PT_INTERP ; p_type ; e_phnum, e_shentsize
dd 0 ; p_flags ; e_shnum, e_shstrndx
ehdr.end:
dq interp - ehdr ; p_offset
dq interp, interp ; p_vaddr, p_paddr
dq interp.end - interp ; p_filesz
dq interp.end - interp ; p_memsz
dq 0 ; p_align
%endif
phdr.dynamic:
dd PT_DYNAMIC ; p_type ; e_phnum, e_shentsize
dd 0 ; p_flags ; e_shnum, e_shstrndx
%ifndef USE_INTERP
ehdr.end:
%endif
dq dynamic - ehdr ; p_offset
dq dynamic, 0 ; p_vaddr, p_paddr
dq dynamic.end - dynamic ; p_filesz
dq dynamic.end - dynamic ; p_memsz
dq 0 ; p_align
%ifndef USE_NX
phdr.load:
dd PT_LOAD ; p_type
dd PHDR_R | PHDR_W | PHDR_X ; p_flags
dq 0 ; p_offset
dq ehdr, 0 ; p_vaddr, p_paddr
dq _smol_total_memsize ; p_filesz
dq _smol_total_memsize ; p_memsz
dq 0x1000 ; p_align
%else
%error "TODO" ; TODO
%endif
%endif
%ifdef USE_INTERP
interp:
db "/lib64/ld-linux-x86-64.so.2", 0
interp.end:
%endif
dynamic:
dynamic.strtab:
dq DT_STRTAB ; d_tag
dq _symbols ; d_un.d_ptr
dynamic.symtab:
dq DT_SYMTAB ; d_tag
dq 0 ; d_un.d_ptr

21
src/linkscr.inc Normal file
View File

@ -0,0 +1,21 @@
; vim: set ft=nasm:
extern _smol_origin
extern _smol_total_size
extern _smol_text_start
extern _smol_text_off
extern _smol_text_end
extern _smol_text_size
extern _smol_textandheader_size
extern _smol_data_start
extern _smol_data_off
extern _smol_data_end
extern _smol_data_size
extern _smol_total_filesize
extern _smol_bss_start
extern _smol_bss_off
extern _smol_bss_end
extern _smol_bss_size
extern _smol_dataandbss_size
extern _smol_total_memsize

View File

@ -6,6 +6,7 @@
_smol_start:
push edx ; _dl_fini
; try to get the 'version-agnostic' pffset of the stuff we're
; interested in
mov ebx, eax
@ -15,7 +16,7 @@ _smol_start:
cmp dword eax, _smol_start
jne short .looper
sub esi, ebx
sub esi, LM_ENTRY_OFFSET_BASE+4 ; +4: take inc-after from lodsb into acct
sub esi, LM_ENTRY_OFFSET_BASE+4 ; +4: take inc-after from lodsd into acct
xchg ebp, ebx
xchg ebx, esi
@ -30,24 +31,17 @@ link: ; (struct link_map *root, char *symtable)
mov esi, [ebp + LM_NAME_OFFSET]
.basename: ; (const char *s (esi))
push esi
push edi
mov edi, esi
.basename.cmp:
lodsb
or al, al
jz short .basename.done
cmp al, 47 ; '/'
cmp al, '/'
cmove edi, esi
jmp short .basename.cmp
or al, al
jnz short .basename.cmp
.basename.done:
xchg eax, edi
pop edi
pop esi
.basename.end:
mov edi, eax
pop esi
.strcmp: ; (const char *s1 (esi), const char *s2 (edi))
push esi
push edi
@ -77,7 +71,7 @@ link: ; (struct link_map *root, char *symtable)
.do_symbols: ; null byte means end of symbols for this library!
lodsb
test al, al
jz short .next_library
jz short .do_library
push ebx
xchg ebx, edi
@ -89,7 +83,7 @@ link: ; (struct link_map *root, char *symtable)
xor edx, edx
mov ebx, [ebp + edi + LM_NBUCKETS_OFFSET]
div ebx
; eax = entry->l_gnu_buckets[eax]
; eax = entry->l_gnu_buckets[edx]
mov eax, [ebp + edi + LM_GNU_BUCKETS_OFFSET]
mov eax, [eax + edx * 4]
; *h |= 1
@ -119,14 +113,13 @@ link: ; (struct link_map *root, char *symtable)
add esi, 4
jmp short link.do_symbols
inc esi
link.next_library:
jmp link.do_library
link.done:
;xor ebp, ebp ; let's put that burden on the user code, so they can leave
; it out if they want to
sub esp, 20 ; put the stack where _stack (C code) expects it to be
pop edx ; _dl_fini
sub esp, 20 ; put the stack where _start (C code) expects it to be
; this can't be left out, because X needs the envvars
;.loopme: jmp short .loopme

172
src/loader64.asm Normal file
View File

@ -0,0 +1,172 @@
; vim: set ft=nasm:
%include "rtld.inc"
%ifdef ELF_TYPE
[section .text.startup.smol]
%else
; not defined -> debugging!
[section .text]
%endif
; rax: special op reg
;!rbx: ptrdiff_t glibc_vercompat_extra_hi_field_off
; rcx: special op reg
; rdx: special op reg
; rsi: special op reg
; rdi: struct link_map* root / special op reg
; rbp:
; r8 :
; r9 :
;!r10: struct link_map* entry + far correction factor
; r11: temp storage var
;!r12: struct link_map* entry
;!r13: _dl_fini address (reqd by the ABI)
%ifndef ELF_TYPE
extern _symbols
global _start
_start:
%endif
_smol_start:
xchg r13, rdx ; _dl_fini
mov r12, [rsp - 8] ; return address of _dl_init
mov r11d, dword [r12 - 20] ; decode part of 'mov rdi, [rel _rtld_global]'
mov r12, [r12 + r11 - 16] ; ???
; struct link_map* root = r12
;mov r12, rdi
mov rsi, r12
; size_t* field = (size_t*)root;
; for (; *field != _smol_start; ++field) ;
.next_off:
lodsq
cmp rax, _smol_start
jne short .next_off
; // rbx = offsetof(struct link_map* rsi, l_entry) - DEFAULT_OFFSET
; rbx = field - root - offsetof(struct link_map, l_entry)
sub rsi, r12
sub rsi, LF_ENTRY_OFF+8
xchg rbx, rsi
mov rsi, _symbols
; for (rsi = (uint8_t*)_symbols; *rsi; ++rsi) {
.next_needed:
cmp byte [rsi], 0
je .needed_end
; do { // iter over the link_map
.next_link:
; entry = entry->l_next;
mov r12, [r12 + L_NEXT_OFF] ; skip the first one (this is our main
; binary, it has no symbols)
; keep the current symbol in a backup reg
mov rdx, rsi
; r11 = basename(rsi = entry->l_name)
mov rsi, [r12 + L_NAME_OFF]
.basename:
mov r11, rsi
.basename.next:
lodsb
cmp al, '/'
cmove r11, rsi
or al, al
jnz short .basename.next
.basename.done:
; and place it back
mov rsi, rdx ; rsi == _symbol
; strcmp(rsi, r11) -> flags; rsi == first hash if matches
.strcmp:
lodsb
or al, al
jz short .strcmp.done
sub al, byte [r11]
cmovnz rsi, rdx
jnz short .next_link;.strcmp.done
inc r11
jmp short .strcmp
.strcmp.done:
;mov rsi, rdx
; if (strcmp(...)) goto next_link;
;cmovnz r12, [r12 + L_NEXT_OFF] ; this is guaranteed to be nonzero
;jnz short .next_link ; because otherwise ld.so would have complained
; now we have the right link_map of the library, so all we have
; to do now is to find the right symbol addresses corresponding
; to the hashes.
; do {
.next_hash:
; if (!*phash) break;
lodsq
or eax, eax
jz short .next_needed ; done the last hash, so move to the next lib
;link_symbol(struct link_map* entry = r12, size_t* phash = rsi, uint32_t hash = eax)
lea r10, [r12 + rbx]
mov r11, rax
; uint32_t bkt_ind(edx) = hash % entry->l_nbuckets
xor edx, edx
mov ecx, dword [r10 + LF_NBUCKETS_OFF]
div ecx
; shift left because we don't want to compare the lowest bit
shr r11, 1
; uint32_t bucket(edx) = entry->l_gnu_buckets[bkt_ind]
mov r8, [r10 + LF_GNU_BUCKETS_OFF]
mov edx, dword [r8 + rdx * 4]
; do {
.next_chain:
; uint32_t luhash(ecx) = entry->l_gnu_chain_zero[bucket] >> 1
mov rcx, [r10 + LF_GNU_CHAIN_ZERO_OFF]
mov ecx, dword [rcx + rdx * 4]
shr ecx, 1
; if (luhash == hash) break;
cmp ecx, r11d
je short .chain_break
; ++bucket; } while (1);
inc edx
jne short .next_chain
.chain_break:
; ElfW(Sym)* symtab = entry->l_info[DT_SYMTAB]->d_un.d_ptr
; ElfW(Sym)* sym = &symtab[bucket]
; *phash = sym->st_value + entry->l_addr
; ElfW(Dyn)* dyn(rax) = entry->l_info[DT_SYMTAB]
mov rax, [r12 + L_INFO_DT_SYMTAB_OFF]
; ElfW(Sym)* symtab(rax) = dyn->d_un.d_ptr
mov rax, [rax + D_UN_PTR_OFF]
; ElfW(Addr) symoff(rax) = symtab[bucket].st_value
lea rdx, [rdx + rdx * 2]
mov rax, [rax + rdx * 8 + ST_VALUE_OFF]
; void* finaladdr(rax) = symoff + entry->l_addr
mov rcx, [r12 + L_ADDR_OFF]
add rax, rcx
; *phash = finaladdr
mov [rsi-8], rax
; } while (1)
jmp short .next_hash
.needed_end:
;xor rbp, rbp
mov rdi, rsp
push rax
xchg rsi, r13 ; _dl_fini
; fallthru to _start

View File

@ -1,5 +1,6 @@
; vim: set ft=nasm:
%if __BITS__ == 32
%define LM_NAME_OFFSET 0x4
%define LM_NEXT_OFFSET 0xC
%define LM_ADDR_OFFSET 0
@ -15,6 +16,29 @@
%define DT_VALUE_OFFSET 0x4
%define DYN_PTR_OFFSET 0x4
%define DT_SYMTAB 0x6
%define DT_SYMSIZE_SHIFT 4
%define DT_SYMTAB 0x6
%else
%define L_ADDR_OFF ( 0)
%define L_NAME_OFF ( 8)
%define L_NEXT_OFF ( 24)
%define L_INFO_OFF ( 64)
%define L_INFO_DT_SYMTAB_OFF (112)
%define LF_ENTRY_OFF (688)
%define LF_NBUCKETS_OFF (756)
%define LF_GNU_BUCKETS_OFF (776)
%define LF_GNU_CHAIN_ZERO_OFF (784)
%define L_GNU_BUCKETS_SZ ( 4)
%define L_GNU_CHAIN_ZERO_SZ ( 4)
%define D_UN_PTR_OFF ( 8)
%define ST_VALUE_OFF ( 8)
%define ELF_SYM_SZ ( 24)
%define ELF_DYN_SZ ( 16)
%endif

View File

@ -7,7 +7,9 @@ const char *f = "foo";
int main(int argc, char* argv[]) {
printf("hello world %s\n", f);
printf("argc=%d\n", argc);
for (int i = 0; i < argc; ++i) printf("argv[%d]=%s\n", i, argv[i]);
printf("argv=%p\n", (void*)argv);
for (int i = 0; i < argc; ++i)
printf("argv[%d](%p)=%s\n", i, (void*)argv[i], argv[i]);
exit(42);
}

View File

@ -1,8 +1,11 @@
#include <stdio.h>
#include <SDL2/SDL.h>
int main(void) {
SDL_Init(SDL_INIT_VIDEO);
SDL_Window *w = SDL_CreateWindow("nice", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1280, 720, SDL_WINDOW_OPENGL);
SDL_Window *w = SDL_CreateWindow("nice", SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED, 1280, 720, SDL_WINDOW_OPENGL);
printf("%s\n", "ohai");
SDL_Delay(3000);
SDL_DestroyWindow(w);
return 0;