Handling SIGCHLD NASM



edit 请参阅下面的自我答案


我一直在尝试在nasm

中复制此C程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
static void handle(int sig) {
    int status;
    wait(&status);
}
int main(int argc, char* argv[]) {
    struct sigaction act;
    bzero(&act, sizeof(act));
    act.sa_handler = &handle;
    sigaction(SIGCLD, &act, NULL);
    pid_t pid;
    if ( (pid = fork()) == 0) {
        printf("message from childn");
        exit(0);
    }
    printf("message from parentn");
    pause();
    exit(0);
}

我的nasm代码看起来像这样:

USE64
STRUC sigact
    .handler        resq 1
    .mask           resq 16
    .flag           resd 1
    .restorer       resq 1
    .pad            resb 4
ENDSTRUC
section .text
global _start
_start:
    ; register SIGCHLD handler
    mov     rdi, act
    mov     rsi, sigact_size
    call    bzero
    mov     QWORD [act], handle
    ; still need to figure out what these mean
    ; yanked out of gdb right before the same syscall
    ; and the act struct had these set :
    mov     QWORD [act+8], 0x4000000
    mov     DWORD [act+16], 0xf7a434a0
    mov     DWORD [act+20], 0x7fff
    mov     rax, 13
    mov     rdi, 17
    lea     rsi, [act]
    mov     rdx, 0x0
    mov     r10, 0x8
    syscall
    cmp     rax, 0
    jne     sigaction_fail
    mov     rax, 57
    syscall
    cmp     rax, 0
    je      child
    mov     rax, parentmsg
    call    print
    mov     rax, 34
    syscall
    mov     rax, parentexit
    call    print
    mov     rax, 60
    mov     rdi, 0
    syscall

sigaction_fail:
    enter   0, 0
    mov     rax, safailed
    call    print
    mov     rax, 60
    mov     rdi, -1
    syscall
handle:
    enter   0x10, 0
    push    rax
    push    rsi
    push    rdi
    push    rdx
    push    r10
    lea     rsi, [rbp-0x10]
    mov     rax, 61
    mov     rdi, -1
    xor     rdx, rdx
    xor     r10, r10
    syscall
    cmp     rax, -1
    jne     .handle_success
    mov     rax, hdfailed
    call    print
    mov     rax, 60
    mov     rdi, -1
    syscall
.handle_success:
    mov     rax, hdsuccess
    call    print
    pop     r10
    pop     rdx
    pop     rdi
    pop     rsi
    pop     rax
    leave
    ret
child:
    mov     rax, childmsg
    call    print
    mov     rax, 60
    mov     rdi, 0
    syscall
; print a null terminated string stored in rax
print:
    enter   0, 0
    push    rbx
    push    rdx
    push    rdi
    push    rsi
    mov     rbx, rax
    call    strlen
    mov     rdx, rax
    mov     rax, 1
    mov     rdi, 1 ; stdout
    mov     rsi, rbx
    syscall
    pop     rsi
    pop     rdi
    pop     rdx
    pop     rbx
    leave
    ret
bzero:
    ; rdi pointer to uint8_t
    ; uint32_t rsi length
    enter   0, 0
    mov     rcx, rsi
    dec     rcx ; err..
.bzeroloop:
    lea     rax, [rdi + rcx]
    xor     rax, rax
    cmp     rcx, 0
    je      .bzerodone
    dec     rcx
    jmp     .bzeroloop
.bzerodone:
    leave
    ret
strlen:
    enter   0, 0
    push    rbx
    mov     rbx, rax
.strlen_countchar:
    cmp     BYTE [rax], 0 ; compare it to null byte
    jz      .strlen_exit
    inc     rax
    jmp     .strlen_countchar
.strlen_exit:
    sub     rax, rbx
    pop     rbx
    leave
    ret

section .data
    childmsg:   db  "from child", 0xa, 0 ; null terminated
    parentmsg   db  "from parent", 0xa, 0
    handlemsg   db  "in handle", 0xa, 0
    safailed    db  "failed to set signal handler", 0xa, 0
    hdfailed    db  "failed waiting for child", 0xa, 0
    hdsuccess   db  "successfully waited on child", 0xa, 0
    parentexit  db  "parent exiting", 0xa, 0
section .bss
    act:            resb    sigact_size
    status:         resq    1

发送信号时成功地等待孩子,但返回时立即出现故障。我已经尝试阅读越来越多的信号和信号处理,但是此时,它都像糊状的那样一起运行。对不起,NASM代码很丑陋或非标准。我不仅在学习,而且我可能至少重写每个部分25次(handle可能以上100次)。

信号处理程序是正常功能。使用ret返回,而不是iret。(从那以后,您已经编辑了您的问题来解决此问题,所以我想您还有其他问题)。

查看GCC如何编译handler(),在Godbolt编译器资源管理器上。

static void handle(int sig) {
    int status;
    wait(&status);
}
    sub     rsp, 24
    xor     eax, eax           # you forgot to include sys/wait.h, so the compiler has no prototype for wait(), so has to follow the ABI for variadic functions (al = number of FP args in xmm regs)
    lea     rdi, [rsp+12]
    call    wait
    add     rsp, 24
    ret

不难将库调用变成wait4(2)的内联调用。

请注意,MAN页面说wait4已过时,新程序应使用waitpidwaitid。但是,如果您不需要任何功能,则wait4很好。GLIBC在wait4 Linux系统呼叫的顶部实现wait(3),而不是waitid。如果使用wait4有任何问题,则GLIBC将直接使用waitid

handle:
    mov     eax, 61          ; __NR_wait4 from /usr/include/x86_64-linux-gnu/asm/unistd_64.h
    mov     edi, -1          ; pid_t is a 32bit type, so we don't need to sign-extend into rdi.
    lea     rsi, [rsp-4]     ; status = a pointer into the red zone below rsp.
    xor     edx,edx          ; options = 0
    xor     r10d,r10d        ; rusage = NULL
    syscall
    ; eax = pid waited for, or -1 to indicate error
    ; dword [rsp-4] = status.  unlike function calls, syscalls don't clobber the stack
    ret

要使用wait4的返回值,请执行以下操作:

    cmp     rax, -1         ;;;; THIS WAS A BUG: pid_t is a 32bit type; expect garbage or zeros in the upper 32 bits.
    cmp     eax, -1
    je   .error
    ...

如果要调试handle中的断点。这比在ASM中使用调试印刷更容易

如果您在ret时仍然崩溃,也许它正在成功返回,但实际上崩溃了。使用调试器查找。


您的字符串常数应进入.rodata部分。您无需在运行时修改它们,因此请勿将它们放入.data

您也不需要call bzero,因为您的act在BSS中。如果您想在堆栈上分配它,而不是静态上,则应用rep stosq将其归零,例如GCC 5.3在main()中做到。(正如您在Godbolt上看到的那样,它引入了BZERO)。


顺便说一句,您的nasm结构在问题上的填充位置在错误的位置。即使事实证明并不是这个问题的答案的一部分,也许值得注意的是您的未来冒险。(定义后,您的代码甚至都不使用NASM结构语法。)

实际struct sigaction是在/usr/include/x86_64-linux-gnu/bits/sigaction.h中定义的,因为您可以从echo '#include <signal.h>' | gcc -E - | less中找到它。

struct sigaction {
    union {
      __sighandler_t sa_handler;    // your code uses this one, because it leaves SA_SIGINFO unset in sa_flags
      void (*sa_sigaction) (int, siginfo_t *, void *);
    };  // with some macros to sort this out
    __sigset_t sa_mask;   // 1024 bits = 128B
    int sa_flags; 
    void (*sa_restorer) (void);
};

您的NASM结构在错误的位置上有填充:

STRUC sigact
    .handler        resq 1    ; 64bit pointer: correct
    .mask           resq 16   ; 16 qwords for sigset_t: correct, it's 128 bytes
    .flag           resd 1    ; 32bit flags: correct
    ;; The padding goes here, to align the 64bit member that follows
    .pad            resb 4
    .restorer       resq 1    ; 64bit
    ;; There's no padding here
ENDSTRUC

oooook长时间戳戳后,我终于弄清楚了!问题是在Sigact结构中正确设置了修复程序。

当我检查sigaction(2)以获取结构定义时,它根本不是我认为的。我知道了:

struct sigaction {
    void     (*sa_handler)(int);
    void     (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t   sa_mask;
    int        sa_flags;
    void     (*sa_restorer)(void);
};

但这是结构的C定义(嗯,这还不是很大,因为人页面提到了前两个可能是我的结合)。

但是,我发现我需要建造的结构看起来更像是这样:

struct asm_sigaction {
    void                  (*sa_handler)(int);
    [unsigned?] long      sa_flags;
    void                  (*sa restorer)(void);
    sigset_t              sa_mask; 
};

我通过在我的C代码真正做的事情中四处挖掘这一点。我找到了我正在制作的同一syscall的位置,并将其用于Sigaction Struct的字节:

(gdb) x/38wx $rsi
0x7fffffffddc0: 0x004007f5  0x00000000  0x14000000  0x00000000
0x7fffffffddd0: 0xf7a434a0  0x00007fff  0x00000000  0x00000000
0x7fffffffdde0: 0x00000000  0x00000000  0x00000000  0x00000000
0x7fffffffddf0: 0x00000000  0x00000000  0x00000000  0x00000000
0x7fffffffde00: 0x00000000  0x00000000  0x00000000  0x00000000
0x7fffffffde10: 0x00000000  0x00000000  0x00000000  0x00000000
0x7fffffffde20: 0x00000000  0x00000000  0x00000000  0x00000000
0x7fffffffde30: 0x00000000  0x00000000  0x00000000  0x00000000
0x7fffffffde40: 0x00000000  0x00000000  0x00000000  0x00000000

0x7fffffffddd0的零件看起来像是我的地址,所以我检查了一下:

(gdb) disas 0x00007ffff7a434a0
Dump of assembler code for function __restore_rt:
   0x00007ffff7a434a0 <+0>: mov    rax,0xf
   0x00007ffff7a434a7 <+7>: syscall 
   0x00007ffff7a434a9 <+9>: nop    DWORD PTR [rax+0x0]

肯定的是,他们设置了调用sigreturn(在我的情况rt_sigreturn)系统调用的修复程序!男人页面说,应用程序通常不会对此感到困惑,但这是我猜的典型C程序。因此,我继续在修复器标签中复制此功能,并将其放在我的Struc和Wooooooo中的适当位置。

这是现在正在工作的nasm,我使用一个新的C程序更改了一些问题,我试图使外观和表现更像我的NASM程序,并将pause转换为nanosleep

新C程序:

#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>
const char *parentmsg = "from parentn";
const char *childmsg = "from childn";
const char *handlemsg = "in handlen";
const char *forkfailed = "fork failedn";
const char *parentexit = "parent exitingn";
const char *sleepfailed = "sleep failedn";
const char *sleepinterrupted = "sleep interruptedn";
void print(const char *msg) {
    write(STDIN_FILENO, msg, strlen(msg));
}
static void handle(int sig) {
    print(handlemsg);
    waitid(P_ALL, -1, NULL, WEXITED|WSTOPPED|WCONTINUED);
}
int main(int argc, char* argv[]) {
    struct timespec tsreq;
    struct timespec tsrem;
    tsreq.tv_sec = 2;
    struct sigaction act;
    act.sa_handler = &handle;
    sigaction(SIGCHLD, &act, NULL);
    pid_t pid;
    if ( (pid = fork()) == 0 ) {
        print(childmsg);
        exit(0);
    }
    print(parentmsg);
    if (nanosleep((const struct timespec*)&tsreq, &tsrem) == -1) {
        if (errno == EINTR) {
            print(sleepinterrupted);
            nanosleep((const struct timespec*)&tsrem, NULL);
        } else {
            print(sleepfailed);
        }
    }
    print(parentexit);
    exit(0);
}

和新的工作nasm(在彼得的一些帮助下,希望它看起来和功能更好)

USE64
STRUC sigact
    .handler        resq 1
    .flag           resq 1
    .restorer       resq 1
    .mask           resq 16
ENDSTRUC
STRUC timespec
    .tv_sec         resq 1
    .tv.nsec        resq 1
ENDSTRUC
section .text
global _start
_start:
    ; register SIGCHLD handler
    mov     DWORD [act+sigact.handler], handle
    mov     QWORD [act+sigact.restorer], restorer
    mov     DWORD [act+sigact.flag], 0x04000000
    mov     rax, 13
    mov     rdi, 17
    lea     rsi, [act]
    xor     rdx, rdx
    mov     r10, 0x8
    syscall
    cmp     eax, 0
    jne     sigaction_fail
    mov     rax, 57
    syscall
    cmp     eax, -1
    je      fork_failed
    cmp     eax, 0
    je      child
    mov     rax, parentmsg
    call    print
    mov     rax, 35
    mov     QWORD [tsreq+timespec.tv_sec], 2
    lea     rdi, [tsreq]
    lea     rsi, [tsrem]
    syscall
    cmp     eax, -1
    je      .exit
    mov     rax, sleepagain
    call    print
    mov     rax, 35
    mov     rdi, tsrem
    xor     rsi, rsi
    syscall
.exit:
    mov     rax, parentexit
    call    print
    mov     rax, 60
    xor     rdi, rdi
    syscall

restorer:
    mov     rax, 15
    syscall
fork_failed:
    mov     rax, forkfailed
    call    print
    mov     rax, 60
    mov     rdi, -1
    syscall
sigaction_fail:
    mov     rax, safailed
    call    print
    mov     rax, 60
    mov     rdi, -1
    syscall
handle:
    mov     rax, handlemsg
    call    print
    lea     rsi, [rsp-0x4]
    mov     rax, 247
    xor     rdi, rdi
    xor     rdx, rdx
    mov     r10, 14
    syscall
    cmp     eax, -1
    jne     .success
    mov     rax, hdfailed
    call    print
    mov     rax, 60
    mov     rdi, -1
    syscall
.success:
    mov     rax, hdsuccess
    call    print
    ret
child:
    mov     rax, childmsg
    call    print
    mov     rax, 60
    xor     rdi, rdi
    syscall
; print a null terminated string stored in rax
print:
    push    rbx
    push    rdx
    push    rdi
    push    rsi
    mov     rbx, rax
    call    strlen
    mov     rdx, rax
    mov     rax, 1
    mov     rdi, 1 ; stdout
    mov     rsi, rbx
    syscall
    pop     rsi
    pop     rdi
    pop     rdx
    pop     rbx
    ret
strlen:
    push    rbp
    mov     rbp, rsp
    push    rbx
    mov     rbx, rax
.countchar:
    cmp     BYTE [rax], 0 ; compare it to null byte
    jz      .exit
    inc     rax
    jmp     .countchar
.exit:
    sub     rax, rbx
    pop     rbx
    mov     rsp, rbp
    pop     rbp
    ret

section .data
    childmsg:   db  "from child", 0xa, 0 ; null terminated
    parentmsg   db  "from parent", 0xa, 0
    handlemsg   db  "in handle", 0xa, 0
    safailed    db  "failed to set signal handler", 0xa, 0
    hdfailed    db  "failed waiting for child", 0xa, 0
    hdsuccess   db  "successfully waited on child", 0xa, 0
    parentexit  db  "parent exiting", 0xa, 0
    forkfailed  db  "fork failed", 0xa, 0
    sleepagain  db  "sleeping again", 0xa, 0
section .bss
    tsreq:          resb    timespec_size
    tsrem:          resb    timespec_size
    act:            resb    sigact_size

相关内容

  • 没有找到相关文章

最新更新