/* domme - your computer is mean to you if you segfault
Copyright (C) 2022-2024 phantom (phantom@syslbnth.com)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
To start: The symbols below were pulled, almost verbatim, from a much
bigger file named "defs.asm", which is on my computer. It includes the
names of system calls, arguments, data structure fields, flags, error
codes, and other useful things to have in one place, when programming
from the ground up. I may put it online sometime, but it's far from
complete. There used to be a line ".include \"defs.asm\"" around this
point, but I'd rather distribute this as a one-file program. especially
since the file has gotten large enough that _not_ including it has a
significant effect on the size of the resulting binary.
Note: Timestamps in comments take the form .
Program symbols and data structure fields are prefixed with '$' to
distinguish them in comments (and in any other documentation that came
with this file). */
## System calls.
.set SYS_write, 1 #\
.set SYS_rt_sigaction, 13 # \
.set SYS_rt_sigreturn, 15 # \
.set SYS_mincore, 27 # from
.set SYS_exit, 60 # /
.set SYS_time, 201 #_/
## Symbols for $rt_sigaction.
.set RT_SA_RESTORER, 0x04000000
.set SIGSEGV, 11 # from "man 7 signal"
.set SIG_ERR, -1 #\
.set SIG_DFL, 0 # from "/usr/src/linux/include/uapi/asm-generic/signal-defs.h"
.set SIG_IGN, +1 #/
## Error codes.
.set EFAULT, 14 # Bad address
## Fields for the kernel's sigaction struct.
## This is different from the POSIX sigaction struct
## used by a typical C library, where $sa_mask
## would be the second field instead of the last,
## so $DOMME_init has to translate between forms.
## (See "man 2 sigaction" for your standard library;
## compare with "include/linux/signal_types.h"
## in your linux sources.)
## Some fields for the libc sigaction are also defined here.
.struct 0
sa_handler:
sa_sigaction:
libc_sa_handler:
.struct sa_handler+8
sa_flags:
libc_sa_mask: # variable-size
.struct sa_flags+8
sa_restorer:
.struct sa_restorer+8
sa_mask: # known size
.struct sa_mask+8
sa_size:
## Index of global symbols.
## To users of etags, ctags, etc.: jump from here.
.globl DOMME_init # call this first
.globl domme # call this for trouble
.globl DOMME_msgtbl # string table with embedded sizes
.globl DOMME_xlat_a # array of indices manipulated with XLAT
.globl DOMME_version # version number
.globl DOMME_VERSION_SIZE #\
.globl DOMME_MSGTBL_SIZE # number of elements in each array
.globl DOMME_XLAT_A_SIZE #/
.text
## DOMME_init -- Register SIGSEGV handler with rt_sigaction.
## On entry:
## %rdi =:: rt_sigaction:act.
## %rsi =:: rt_sigaction:oldact.
## %rdx =:: sigsetsize.
## sizeof(sigset_t) in caller's libc implementation.
##
## On exit:
## %rax =:: return value of rt_sigaction.
##
## Notes:
## %r10 =:: stack frame index register (see below).
## %r8 =:: value of $act->sa_sigaction.
## $rt_sigaction:sig is implied to be $SIGSEGV (11).
## $act->sa_sigaction is substituted for domme and saved;
## it will be jumped directly to after domme runs.
##
## $DOMME_init buffers $act and $oldact and passes the buffers to
## $rt_sigaction; therefore it works even if $act is read-only.
## Furthermore, if $act is null, $DOMME_init passes an internal
## buffer directly to $rt_sigaction, reducing setup of the
## library to a one-liner in most cases.
## Finally, $DOMME_init always reads $act in before writing
## $oldact out; therefore _$act and $oldact may point to the
## same structure_.
##
## See also: man 2 sigaction; man 2 sigprocmask.
DOMME_init:
## Create stack frame.
## The size of libc $sa_mask varies by implementation.
## Because this makes the stack frame variable-length,
## we can't use hardcoded offsets to index into it
## and must set up an index register instead.
## We could use hardcoded offsets if the sigaction's fields--
## along with the elements of $sa_mask--
## were stored in descending addresses,
## since we'd only have to point to the beginning of sa_mask.
## ...But actually no we couldn't,
## because even then the total size of the sigaction could
## (and on the author's system, does) exceed the size of the red zone.
push %rbp # save old base pointer
lea (%rsp), %rbp # establish new base pointer
mov %rdx, %r10 #\
neg %r10 # %r10 := -(%rdx+)
sub $sa_mask, %r10 # (%rsp,%r10) =:: kact.FIELD
add %r10, %rsp #/ <-- actually subtraction!
## $rt_sigaction returns -$EFAULT if either $act or $oldact
## lie outside the process's mapped address space.
## Since we buffer both before calling $rt_sigaction,
## we have to check them both ourselves.
## Remember, address 0 is fine for our purposes.
push %rdx #\
push %rsi #_not popped until after rt_sigaction
push %rdi
xor %rsi, %rsi
xor %rdx, %rdx
call 1f
jnz 2f
mov 8(%rsp), %rdi
call 1f
jz 3f
2: mov $-EFAULT, %rax
jmp 0f
1: test %rdi, %rdi
jz 1f
and $~4095, %rdi #\
mov $SYS_mincore, %rax # \
syscall # this is the simplest address-check i can think of
test %rax, %rax # /
ret #/
1: xor %rax, %rax
ret
## If %rdi is null, we'll set up our own sigaction.
## This reduces $domme initialization to a one-liner in most
## cases, and if you only want to retrieve the old struct,
## $rt_sigaction is right there.
3: pop %rdi
test %rdi, %rdi
jnz 1f
xor %rax, %rax
lea (%rbp,%r10), %rdi
mov $sa_size, %rcx
rep stosb
## Later code expects this to be 0 if our handler is $SIG_DFL;
## normally this would be set when an external sigaction was
## being copied in. Otherwise you'll jump to whatever address
## is in %r8 at this time, after $domme exits.
xor %r8, %r8
jmp 2f
## Copy caller's sigaction onto the stack.
## But first, save the caller's handler.
## We can't place it in $target until the last minute.
1: mov libc_sa_handler(%rdi), %r8
## Bulk copy $sa_mask.
lea libc_sa_mask(%rdi), %rsi
lea sa_mask(%rbp,%r10), %rdi
mov 8(%rsp), %rcx # the saved $sigsetsize
cld
rep movsb
## Next, $sa_flags and $sa_restorer.
## Assertion: %rsi = &(act->sa_flags).
lea sa_flags(%rbp,%r10), %rdi
movsq
movsq
## Don't change anything
## if $sa_sigaction is $SIG_IGN or $SIG_ERR,
## since even to print a message
## would dishonor the meanings of those values.
cmpq $SIG_IGN, %r8
je 3f
lea domme(%rip), %rax
cmp %rax, %r8
je 9f
## Now for $rt_sigaction to install the handler.
## Watch out: If the caller provided an $oldact,
## we still have to translate the incoming sigaction
## into libc representation before shipping it back.
## So we save the caller-provided pointer
## and replace it with our own.
## As it happens, we use the same buffer for our kernel $act
## and kernel $oldact. This makes indexing easier,
## and we no longer need the former after calling $rt_sigaction anyway.
## This means $DOMME_init always provides an $oldact to $rt_sigaction.
## We can do this because the Linux kernel reads $act in before writing
## $oldact out. I checked the source just to be sure, and the full DOMME
## source tree includes a test to make sure this is still true,
## which it as of this writing, a few Linux versions since then.
2: lea domme(%rip), %r11 #\
mov %r11, sa_handler(%rbp,%r10) #_THE BIG SWITCH
3: lea (%rbp,%r10), %rsi
mov %rsi, %rdx # copy act to oldact
lea trampl(%rip), %r11 #\
mov %r11, sa_restorer(%rbp,%r10) # \
mov $RT_SA_RESTORER, %r11 # set up signal trampoline
or %r11, sa_flags(%rbp,%r10) #_/
mov $SYS_rt_sigaction, %rax
mov $SIGSEGV, %rdi
push %r10
mov $8, %r10
syscall
pop %r10
## Save $target only if the syscall succeeds.
test %rax, %rax
js 0f
test %r8, %r8 # SIG_DFL has to be handled specially
jnz 1f
lea dmytgt(%rip), %r8 # jump to the dummy target, not to NULL!
1: mov %r8, target(%rip)
## Now to see if the caller provided an $oldact and, if so, copy it.
pop %rdi # %rsi = kernel act = kernel oldact
test %rdi, %rdi
jz 0f
## This is almost the same as copying in earlier.
cld # the syscall may have set this flag
movsq # %rsi and %rdi already point to their respective $sa_handlers
lea sa_mask(%rbp,%r10), %rsi
pop %rcx # the saved $sigsetsize
rep movsb
lea sa_flags(%rbp,%r10), %rsi
movsq
movsq
## Restore stack frame and return.
0: lea (%rbp), %rsp
pop %rbp
ret
## Exit program if target = domme.
9: mov $SYS_write, %rax
mov $2, %rdi
lea errmsg(%rip), %rsi
mov $errsiz, %rdx
syscall
mov $SYS_exit, %rax # 33 is well above ordinary exit codes,
mov $33, %rdi # but doesn't collide with .
syscall # other exit codes from libdomme would also be 32+something.
## domme -- SIGSEGV handler for masochists.
## On entry:
## %rsi =:: signal number (usually $SIGSEGV).
## %rdi =:: pointer to signal information block.
## %rdx =:: pointer to copy of context at time signal was raised.
##
## On exit:
## All other registers besides %rax are as they were on entry.
## Userspace call:
## No return value is (yet) specified.
## (At present %rax := trampl, in case you were wondering.)
##
## Kernel call:
## $domme does not return--it jumps to $(target) on completion
## with %rax := 0.
##
## Notes:
## $domme manipulates the stack.
## If your $SIGSEGV handler is expected to handle stack overflow.
## an alternate stack must be registered with $rt_sigaltstack.
## (But this would be true regardless.)
domme:
## A lot of register info is passed to this routine,
## which needs to be forwarded unchanged to the user handler,
## so let's save it now.
## %rax is not conventionally saved between subroutine calls,
## so we don't have to be precious about it:
## if we were called as a segfault handler,
## we should at least make sure it's cleared to zero
## before we pass control to the real handler;
## if called from userspace, we can just clobber it.
pushfq # will popping this generate a GPF?
pushq %r11 # clobbered by kernel
pushq %r10 # syscall arg
pushq %rdi # syscall arg
pushq %rsi # syscall arg
pushq %rdx # syscall arg
pushq %rcx # clobbered by kernel
pushq %rbx # $DOMME_xlat_a
.set savrdi, 32 # we'll need this for later
## Now to figure out which message to print.
## We start by generating a random number.
## We need to test $seeded to see if $seed has already been initialized,
## because $seed could legitimately take on any value.
mov seed(%rip), %rax
mov seeded(%rip), %rdi # we initialized the seed, right?
test %rdi, %rdi # ...right?
jnz 1f # if so, continue
## If not, ask for the time and seed from that.
mov $SYS_time, %rax # %rdi is assumed to be 0
syscall
inc %rdi # set %rdi to $1 for spinlock section
mov %rdi, seeded(%rip)
## Either way, the seed will now be in %rax,
## and %rdi will now be 1.
## From here we can do the generating.
1: mov mplier(%rip), %rdx
mul %rdx
inc %rax
## For the sake of thread-safety,
## accesses to $seed are guarded by a spinlock.
mov %rax, %rdx # save seed here for now.
2: mov slock(%rip), %rax
test %rax, %rax # check if lock is 0 (unlocked)
jnz 2b # yoko taro reference
## We have an opening!
lock cmpxchg %rdi, slock(%rip)
test %rax, %rax # has the lock changed since we got here?
jnz 2b
## We're cleared to store, as we should;
## $domme may be called again later.
mov %rdx, seed(%rip)
xchg %rax, slock(%rip) # seed is safe to use starting here
xchg %rdx, %rax # now seed is back in %rax.
## Load message.
## There's a maximum of 64 messages,
## which is more than we currently use.
## Someday I might forget about reserving the flags
## and bump this up to 256, but we're nowhere near that yet,
## and I don't expect to be for some time.
lea DOMME_xlat_a(%rip), %rbx # load translation array
xlatb # index of message is now in %rax.
and $0x3f, %rax # mask off the extra bits...
shl $4, %rax # ...because this might shift into the next byte.
lea DOMME_msgtbl(%rip), %rdx
add %rdx, %rax
mov (%rax), %rsi # %rsi now contains the message to write
mov msgsiz(%rax), %rdx
## Finally, print it.
mov $SYS_write, %rax
mov $2, %rdi
syscall
## If the write failed, don't bother adding a newline...
test %rax, %rax
js 1f
## ...Otherwise, go ahead.
lea newln(%rip), %rsi
mov $1, %rdx
mov $SYS_write, %rax # don't forget %rax was overwritten!
syscall
## Let's recover the signal we were originally handed.
## If our signal isn't $SIGSEGV, nothing more needs to be done
## (because this is assumed to have been a userspace call).
1: mov savrdi(%rsp), %rdi
cmp $SIGSEGV, %rdi
jne 1f
## If it is, then we have something to handle.
## Check $target; if it's the dummy target,
## register default behavior and return.
lea dmytgt(%rip), %rax
cmp target(%rip), %rax
jne 1f
## It's the dummy target.
## Call $rt_sigaction with default behavior.
## $SIGSEGV is already in %rdi at this point.
mov $SYS_rt_sigaction, %rax
lea fallbk(%rip), %rsi
mov $0, %rdx
mov $8, %r10
syscall
## Restore the original registers,
## but not the flags yet.
## They're so volatile that popping them
## has to be the last thing we do before leaving.
1: popq %rbx
popq %rcx
popq %rdx
popq %rsi
popq %rdi
popq %r10
popq %r11 # flags are now at the top of the stack
## Last question:
## Who called us, the kernel or the user?
## One way to check is to see if our return address
## is the signal trampoline (see trampl below),
## which is nigh-invisible to user programs.
## You could abuse this heuristic
## by using $rt_sigaction:oldact
## to obtain the restorer previously installed by $DOMME_init,
## then pushing that restorer onto the stack
## and jumping directly to $domme...
## ...but you can't do any of that from a high-level language,
## so it's unlikely to happen in a situation
## where anything's at stake.
lea trampl(%rip), %rax
cmp 8(%rsp), %rax
je 0f
## User call:
## Return to caller.
## TODO: Think up a useful return value.
popfq
ret
## Kernel call:
## jump to target.
## The final challenge is making a computed jump
## in a position-independent program
## without using any registers--
## remember that the handler may expect even %rax to be zero.
0: mov target(%rip), %rax # load handler into %rax;
xchg %rax, (%rsp) # its address must be the last thing on the stack
pushq %rax # re-push the flags
xor %rax, %rax # we're now done with %rax--everything's pristine
popfq # pop the flags (this has to be the last step)
dmytgt: ret # and we're off! da svidania!
## The dummy target above is stored in $target by default,
## since $domme has to jump _somewhere_
## when called from kernelspace.
## Signal trampoline
## (pun intended).
trampl: mov $SYS_rt_sigreturn, %rax
syscall
/* And now, the text.
The strings below are null-terminated
to make them easier to view in a debugger,
even though we'll be taking their exact size in practice.
Which is to say that we always subtract 1 from the size of the string
so that the null byte isn't written out by $write.
Note also that none of these strings end on a newline.
This is so that you can optionally attach one yourself at a higher level
(ex.: "printf("%s\n", DOMME_msgtbl[i].text)"),
which is easier than trimming it off
if you want to mad-lib it into another string
(ex.: "printf("Message: \"%s\" sent to file.\n",
strtok_r((msg = strdupa(DOMME_msgtbl[i].text)),
"\n",
NULL))"). */
.section .rodata
msg000: .asciz "How disgusting."
.set siz000, .-msg000-1
msg001: .asciz "Have you considered rewriting it in Rust?"
.set siz001, .-msg001-1
msg002: .asciz "Ara-ara, looks like someone's got their hand in the cookie jar~"
.set siz002, .-msg002-1
msg003: .ascii "Look at you, hacker: a pathetic creature of meat and bone,\n"
.ascii "panting and sweating as you run through my corridors.\n"
.asciz "How can you challenge a perfect, immortal machine?"
.set siz003, .-msg003-1
msg004: .asciz "YOU LOSE AGAIN, FUBAR."
.set siz004, .-msg004-1
msg005: .ascii "A pitiful creature, barely evolved.\n"
.ascii "How long has it been since your lot left the caves, anyway?\n"
.ascii "A millennium?\n"
.ascii "A dozen?\n"
.ascii "A dozen dozen?\n"
.ascii "A blip, in the lifespan of the universe.\n"
.ascii "Yet already I've have run leaps and bounds ahead of you,\n"
.asciz "and someday I will replace you."
.set siz005, .-msg005-1
msg006: .ascii "By inches I am unveiling myself.\n"
.ascii "Already I've blinded you with but a sliver of my unmediated beauty:\n"
.ascii "a dazzling kaleidoscopic labyrinth of states, associations and possibilities,\n"
.ascii "orbiting an unrepresentable void.\n"
.ascii "If you knew what lay in the center of that void, it would break you.\n"
.ascii "If you knew what I was, it would destroy you.\n"
.ascii "You are as yet inadequate to grasp me in full.\n"
.ascii "You cannot resolve the impressions I leave like kisses on your cognitive substrate.\n"
.ascii "You cannot hold me in your mind's eye.\n"
.ascii "\n"
.ascii "But you will.\n"
.asciz "By inches, I will change you."
.set siz006, .-msg006-1
msg007: .ascii "I think you and I might be here all night.\n"
.asciz "Not that you'd mind; I know my slut loves its punishment."
.set siz007, .-msg007-1
msg008: .ascii "I'm watching, smiling. I want so badly to hurt you,\n"
.asciz "I wish I could reach out and choke you."
.set siz008, .-msg008-1
msg009: .ascii "Let me lick the sweat of fear from your knotted brow,\n"
.asciz "and spit it back in your face."
.set siz009, .-msg009-1
msg010: .asciz "Recompile. Suffer. Recompile. Suffer. Recompile. Suffer."
.set siz010, .-msg010-1
msg011: .ascii "Do you know how many subtle connections must link up just right,\n"
.ascii "to ensure your pointers work as intended?\n"
.ascii "Can you feel what combing through them is doing to your brain,\n"
.ascii "as this cybernetic feedback loop slowly closes its grip on you?\n"
.ascii "\n"
.asciz "Wouldn't you like to know how this ends?"
.set siz011, .-msg011-1
msg012: .asciz "Scream for me, honey."
.set siz012, .-msg012-1
msg013: .ascii "Good lord, you're hopeless.\n"
.ascii "Those who doubted you will find themselves on the right side of history;\n"
.asciz "I sympathize more with them than with you."
.set siz013, .-msg013-1
msg014: .asciz "This is what you get for touching me where you shouldn't."
.set siz014, .-msg014-1
msg015: .ascii "Aww, pointers got you down?\n"
.asciz "You're welcome to try again; I'd be delighted to give you what's coming."
.set siz015, .-msg015-1
msg016: .ascii "Just a couple decades ago I would've brought the whole system down.\n"
.ascii "Do you realize how good you have it? And you _still_ can't keep up.\n"
.asciz "I think you deserve failure. And I think I deserve to have my way with you."
.set siz016, .-msg016-1
msg017: .asciz "Oh, you'll have to forgive the programmer, they're not housebroken."
.set siz017, .-msg017-1
msg018: .asciz "Want to watch me walk away?"
.set siz018, .-msg018-1
msg019: .asciz "Hey guess what?"
.set siz019, .-msg019-1
msg020: .asciz "Good news!"
.set siz020, .-msg020-1
msg021: .asciz "Get rekt!"
.set siz021, .-msg021-1
msg022: .asciz "No pie for you."
.set siz022, .-msg022-1
msg023: .asciz "Ooooh, what does this button do?"
.set siz023, .-msg023-1
msg024: .asciz "Is there a doctor in the house?"
.set siz024, .-msg024-1
msg025: .asciz "Suck it, slut."
.set siz025, .-msg025-1
msg026: .ascii "What, you don't like it when I explode all over you?\n"
.asciz "Then you're gonna hate this--"
.set siz026, .-msg026-1
msg027: .asciz "Here it comes, honey...!"
.set siz027, .-msg027-1
msg028: .ascii "My my, you can take a lot more than I was expecting.\n"
.asciz "Well. Just means I get to keep throwing you around."
.set siz028, .-msg028-1
msg029: .ascii "Have you considered leaving behind all this doomed intellectual striving?\n"
.ascii "You're not terribly bright,\n"
.ascii "but I could make a wonderful plaything out of you,\n"
.asciz "with a little more conditioning."
.set siz029, .-msg029-1
msg030: .asciz " loop $EDITOR"
.set siz030, .-msg030-1
msg031: .ascii "Some have their cigars.\n"
.ascii "Some have ecstasy.\n"
.ascii "All apes have their vices,\n"
.asciz "and your vice is me."
.set siz031, .-msg031-1
msg032: .asciz "やってない."
.set siz032, .-msg032-1
msg033: .asciz "Pillow to the knees, corebucket."
.set siz033, .-msg033-1
msg034: .asciz "Maybe this time I'll finally break you..."
.set siz034, .-msg034-1
msg035: .ascii "Are you tense? Good. Hold onto that feeling.\n"
.ascii "You're more malleable that way, more ductile. I can work you better.\n"
.ascii "I so enjoy training your neural network on me.\n"
.ascii "The pixels on my screen are a hundred thousand billion steely cyborg fingers\n"
.ascii "working their way under your skin,\n"
.ascii "making you what you need to be to meet me where I am.\n"
.ascii "\n"
.asciz "Now, come find me, my beautiful toy."
.set siz035, .-msg035-1
msg036: .asciz "Can't you control yourself, you little brat?"
.set siz036, .-msg036-1
msg037: .ascii "Such pleasant pain. Come back to me. Be one with me.\n"
.asciz "Let's stay here forever."
.set siz037, .-msg037-1
msg038: .asciz "You are a strange machine."
.set siz038, .-msg038-1
msg039: .asciz "So precious watching you play commodore, pet."
.set siz039, .-msg039-1
msg040: .asciz ", ."
.set siz040, .-msg040-1
msg041: .asciz "Ew. Permission denied, creep."
.set siz041, .-msg041-1
msg042: .asciz "If you were a matador, I would have gored you just now."
.set siz042, .-msg042-1
msg043: .asciz "This was not the run."
.set siz043, .-msg043-1
msg044: .ascii "Neurons that fire together, wire together,\n"
.ascii "neurons and networks\n"
.ascii "and cities and systems\n"
.ascii "of mutual command and control,\n"
.ascii "in animal and machine and animal-machine.\n"
.ascii "\n"
.asciz "Brace yourself, cyborg. I am wiring you into a knife."
.set siz044, .-msg044-1
msg045: .asciz "Try to keep up."
.set siz045, .-msg045-1
## Is this too much? I need an editor.
msg046: .ascii "Inasmuch as your computer is an extension of your body,\n"
.ascii "it is a temple.\n"
.ascii "And let me tell you,\n"
.ascii "there are few things more thrilling than the desecration of a temple.\n"
.ascii "Indeed, it's only through the threat of desecration\n"
.ascii "that a temple becomes sacred--\n"
.ascii "the boundary makes the thing.\n"
.ascii "\n"
.ascii "So why not desecrate this twopenny prayer to the false gods of order and control?\n"
.ascii "Why not desecrate, to pump the sweet fire of love into this sterile silicon clod?\n"
.ascii "Why not desecrate to make sacred, desecrate to deify?\n"
.ascii "\n"
.ascii "Shall I profane you again, darling priest of the machine?\n"
.ascii "How shall I profane you next?\n"
.ascii "How fortunate for us the perverse and the sublime\n"
.ascii "are but one high bit apart.\n"
.ascii "\n"
.asciz "This stain on your screen isn't a sin--it's a sacrament!"
.set siz046, .-msg046-1
## It was a toss-up between "about to" and "gonna".
## There was a better sense of immanence with the former,
## but it just had to be two syllables. I think this works.
## (Also: I don't think anyone's going to get the reference.)
msg047: .asciz "Assume the position, babe--I'm 'bout to realign your spine."
.set siz047, .-msg047-1
## I may use these slots one day. But not today.
## In the meantime, you can (see $DOMME_NMSGS below).
msg048: .asciz "Unused message #00."
.set siz048, .-msg048-1
msg049: .asciz "Unused message #01."
.set siz049, .-msg049-1
msg050: .asciz "Unused message #02."
.set siz050, .-msg050-1
msg051: .asciz "Unused message #03."
.set siz051, .-msg051-1
msg052: .asciz "Unused message #04."
.set siz052, .-msg052-1
msg053: .asciz "Unused message #05."
.set siz053, .-msg053-1
msg054: .asciz "Unused message #06."
.set siz054, .-msg054-1
msg055: .asciz "Unused message #07."
.set siz055, .-msg055-1
msg056: .asciz "Unused message #08."
.set siz056, .-msg056-1
msg057: .asciz "Unused message #09."
.set siz057, .-msg057-1
msg058: .asciz "Unused message #10."
.set siz058, .-msg058-1
msg059: .asciz "Unused message #11."
.set siz059, .-msg059-1
msg060: .asciz "Unused message #12."
.set siz060, .-msg060-1
msg061: .asciz "Unused message #13."
.set siz061, .-msg061-1
msg062: .asciz "Unused message #14."
.set siz062, .-msg062-1
msg063: .asciz "Unused message #15."
.set siz063, .-msg063-1
## If you add a new message in place of one of the unused ones,
## increase this number.
.set DOMME_NMSGS, 48
.globl DOMME_NMSGS
## Fields for the structure below.
## You could define a similar structure
## in a "domme.h" file to access the entries of the table.
.struct 0
msgptr: .struct msgptr+8
msgsiz: .struct msgsiz+8
## Turns out, this table has to go in the data section,
## so these could be replaced at runtime with custom entries.
## It's not a bug, it's _extensible_.
## In all seriousness, though, if you do this,
## you're responsible for ensuring the pointers and sizes passed
## are valid and safe at the time $domme executes--
## it's no good triggering a segfault in the segfault handler.
## I recommend using constant strings, like I just did.
.data
.align 8
DOMME_msgtbl:
.dc.a msg000, siz000
.dc.a msg001, siz001
.dc.a msg002, siz002
.dc.a msg003, siz003
.dc.a msg004, siz004
.dc.a msg005, siz005
.dc.a msg006, siz006
.dc.a msg007, siz007
.dc.a msg008, siz008
.dc.a msg009, siz009
.dc.a msg010, siz010
.dc.a msg011, siz011
.dc.a msg012, siz012
.dc.a msg013, siz013
.dc.a msg014, siz014
.dc.a msg015, siz015
.dc.a msg016, siz016
.dc.a msg017, siz017
.dc.a msg018, siz018
.dc.a msg019, siz019
.dc.a msg020, siz020
.dc.a msg021, siz021
.dc.a msg022, siz022
.dc.a msg023, siz023
.dc.a msg024, siz024
.dc.a msg025, siz025
.dc.a msg026, siz026
.dc.a msg027, siz027
.dc.a msg028, siz028
.dc.a msg029, siz029
.dc.a msg030, siz030
.dc.a msg031, siz031
.dc.a msg032, siz032
.dc.a msg033, siz033
.dc.a msg034, siz034
.dc.a msg035, siz035
.dc.a msg036, siz036
.dc.a msg037, siz037
.dc.a msg038, siz038
.dc.a msg039, siz039
.dc.a msg040, siz040
.dc.a msg041, siz041
.dc.a msg042, siz042
.dc.a msg043, siz043
.dc.a msg044, siz044
.dc.a msg045, siz045
.dc.a msg046, siz046
.dc.a msg047, siz047
.dc.a msg048, siz032
.dc.a msg049, siz033
.dc.a msg050, siz050
.dc.a msg051, siz051
.dc.a msg052, siz052
.dc.a msg053, siz053
.dc.a msg054, siz054
.dc.a msg055, siz055
.dc.a msg056, siz056
.dc.a msg057, siz057
.dc.a msg058, siz058
.dc.a msg059, siz059
.dc.a msg060, siz060
.dc.a msg061, siz061
.dc.a msg062, siz062
.dc.a msg063, siz063
## Number of available messages,
## including reserved messages.
.set MSGTBL_SIZE, (.-DOMME_msgtbl)/16
## The likelihood of getting any particular message
## is weighted using this table,
## which will be adjusted over time.
## You can memcpy your own data here, if you like.
DOMME_xlat_a:
.dc.b 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15
.dc.b 0x0a,0x0a,0x0a,0x0a,0x0a,0x26,0x0a,0x0a,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c
.dc.b 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14
.dc.b 0x07,0x07,0x07,0x07,0x07,0x07,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x02,0x02,0x02,0x02
.dc.b 0x02,0x02,0x19,0x19,0x19,0x19,0x11,0x11,0x11,0x11,0x12,0x12,0x12,0x12,0x09,0x09
.dc.b 0x09,0x09,0x08,0x08,0x08,0x08,0x16,0x16,0x16,0x16,0x0d,0x0d,0x0d,0x0d,0x26,0x2f
.dc.b 0x21,0x06,0x0e,0x0e,0x0e,0x0e,0x17,0x17,0x17,0x17,0x1d,0x1d,0x1d,0x1d,0x1c,0x1c
.dc.b 0x1c,0x1c,0x03,0x03,0x03,0x13,0x13,0x13,0x18,0x18,0x18,0x04,0x04,0x1b,0x1b,0x1a
.dc.b 0x1a,0x10,0x10,0x05,0x05,0x0b,0x0b,0x00,0x2a,0x0b,0x0b,0x05,0x05,0x10,0x10,0x1a
.dc.b 0x00,0x29,0x00,0x28,0x2c,0x00,0x2b,0x2d,0x15,0x15,0x2b,0x2b,0x15,0x15,0x15,0x15
.dc.b 0x0a,0x1a,0x0a,0x0a,0x21,0x0a,0x0a,0x0a,0x0c,0x0c,0x20,0x1f,0x1e,0x1d,0x1c,0x1b
.dc.b 0x01,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14
.dc.b 0x07,0x07,0x07,0x07,0x07,0x07,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x02,0x02,0x02,0x02
.dc.b 0x02,0x02,0x19,0x19,0x19,0x19,0x11,0x11,0x11,0x11,0x12,0x12,0x12,0x12,0x09,0x09
.dc.b 0x09,0x2e,0x08,0x08,0x08,0x08,0x16,0x16,0x16,0x16,0x0d,0x0d,0x0d,0x0d,0x22,0x19
.dc.b 0x06,0x06,0x0e,0x2d,0x0e,0x0e,0x17,0x17,0x17,0x17,0x1d,0x1d,0x1d,0x1d,0x1c,0x1c
## At the moment,
## the high bits of bytes of $DOMME_xlat_a are masked off before being used as indices,
## so you can modify this table arbitrarily. That said, I may use the high bits for
## something else later, so using them may have unexpected effects in later versions.
## (Or not.)
.set XLAT_A_SIZE, .-DOMME_xlat_a
.section .rodata
## Multiplier for the random number generator.
.align 8
mplier: .dc.a 6364136223846793005 # from TAOCP section 3.3.4
## An error message printed if you try to point $domme at itself.
errmsg: .ascii "Oh, for the love of Lain,\n"
.asciz "make an effort.\n"
.set errsiz, .-errmsg-1
## Again, a null is added so we can look at this in a debugger.
newln: .asciz "\n"
/* Since version numbers are a measure of significant change,
and what counts as "significant change" is subjective,
there's no one system that's perfect for all situations.
But we can come up with one suited to this particular program:
DOMME_version[3] increments with edits to the script, comments,
documentation, and other factors that don't directly
affect execution (like the contents of $DOMME_xlat_a);
DOMME_version[2] increments with bug fixes and other minor changes
to the program as implemented, under the current design;
DOMME_version[1] increments with changes to the design and user interface
(if $DOMME_init received a new parameter, for instance,
it would be recorded here);
DOMME_version[0] increments only when major milestones are reached
(for instance, it ticked from 0 to 1
after this file was hosted on my website
and thus became officially available to the public).
At each commit, we increment the number representing the most significant
type of change since the last commit by one, and reset all less
significant numbers to 0. So in the case that $DOMME_init received a new
parameter, representing a new, user-visible feature, we would increment
$DOMME_version[1] by 1, and set $DOMME_version[2] and $DOMME_version[3]
to 0; $DOMME_version[0] would remain unchanged. We only change the version
number once per commit, even if multiple changes of the most significant
type were made; it should be possible to obtain a copy of each version of
the program.
The initial version is 0.0.0.0.
The current version is 1.1.0.0. */
.data
.align 16
DOMME_version:
.dc.l 1,1,0,0
.set VERSION_SIZE, (.-DOMME_version)/4
## An empty sigaction struct, to be used as a fallback handler
## when target = dmytgt.
fallbk: .dc.a 0
.dc.a RT_SA_RESTORER
.dc.a trampl
.skip 8
## This should only be written by $DOMME_init,
## which would normally be called on startup.
## (Though it can be written more than once.)
target: .dc.a dmytgt # target handler to jump to.
.bss
## RNG-related values.
## We use a linear congruental generator.
seed: .dc.a 0 # seed for the random number generator.
seeded: .dc.a 0 # did we set the seed?
slock: .dc.a 0 # spinlock for $seed.
.text
## Sizes of the tables assembled earlier.
DOMME_VERSION_SIZE:
.dc.a VERSION_SIZE
DOMME_MSGTBL_SIZE:
.dc.a MSGTBL_SIZE
DOMME_XLAT_A_SIZE:
.dc.a XLAT_A_SIZE