An echo program, mostly in C and completely from scratch This is an echo program with no runtime or standard library. It's meant to be compiled with -nostdlib on an amd64 Linux system.

static signed long mywrite(int fd, const void *buf, unsigned long count) {
    signed long retval;
    __asm__ __volatile__(
        "syscall" :
        "=a" (retval) :
        "a" (1), "D" (fd), "S" (buf), "d" (count) :
        "rcx", "r11", "memory"
    );
    return retval;
}

static void myexit(int status) __attribute__((__noreturn__));
static void myexit(int status) {
    __asm__ __volatile__(
        "syscall" :
        :
        "a" (60), "D" (status) :

    );
    __builtin_unreachable();
}

static unsigned long mystrlen(const char *str) {
    const char *pos = str;
    while(*pos) ++pos;
    return pos - str;
}

static void writearg(char *str, char end) {
    unsigned long size = mystrlen(str) + 1;
    unsigned long written = 0;
    str[size - 1] = end;
    do {
        signed long result = mywrite(1, str + written, size - written);
        if(result &lt; 0) myexit(1);
        written += result;
    } while(written &lt; size);
}

void _start(void) __attribute__((__naked__, __noreturn__));
void _start(void) {
    __asm__(
        "pop %rdi\n\t"
        "mov %rsp, %rsi\n\t"
        "jmp mymain"
    );
}

static void mymain(int argc, char *argv[]) __attribute__((__noreturn__, __used__));
static void mymain(int argc, char *argv[]) {
    if(argc &lt;= 1) {
        myexit(mywrite(1, "\n", 1) != 1);
    }
    int i;
    for(i = 1; i &lt; argc - 1; ++i) {
        writearg(argv[i], ' ');
    }
    writearg(argv[i], '\n');
    myexit(0);
}


Some of my concerns:


Is my program's behavior fully compliant with the standard for echo?
Am I making any unwarranted assumptions that could make my code not work on a future release of Linux (or compiler)? In particular, are popping argc and the way I'm overwriting null terminators in argv values okay?
Since my code is Linux-on-amd64-only anyway, are there any other assumptions that I can make? For example, can I assume that Linux will always have continuous argv values, and so just make one big write call after swapping out all the nulls, instead of one per argument? (I know I'd still have to loop write in case of partial writes. I also know I could just copy the strings around myself, but I'd rather write them from where I got them.)
Instead of having _start as an assembly stub and my real code in mymain, is there any way I can put my real code in _start but still be able to safely get ahold of the command-line arguments?
Is my program's behavior fully compliant with the standard for echo?


Code does not process the string as in the OPERANDS section.  In particular:

Code does not support \c: "Suppress the &lt;newline&gt; that otherwise follows the final argument in the output. ..."


  Am I making any unwarranted assumptions that could make my code not work on a future release of Linux (or compiler)? In particular, are popping argc and the way I'm overwriting null terminators in argv values okay?


I see no trouble with argc.

Overwriting the null terminators in argv may/may not be OK, but is not needed.  I could foresee future restrictions.  Alternative: write the argv[i] and then the separator.


  Other issues


No comment.