首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >处理SIGCHLD NASM

处理SIGCHLD NASM
EN

Stack Overflow用户
提问于 2016-06-28 23:39:05
回答 2查看 337关注 0票数 2

编辑见下面的“自我答案”

我一直试图在NASM中复制这个C程序

代码语言:javascript
复制
#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 child\n");
        exit(0);
    }
    printf("message from parent\n");
    pause();
    exit(0);
}

我的NASM代码如下:

代码语言:javascript
复制
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次(也许是100+的handle)。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-06-29 20:59:46

很长一段时间后,我终于想出来了!问题是在sigact结构中正确地设置了恢复器。

当我检查sigaction(2)以获得结构定义时,它最终与我所认为的完全不同。我拿到了这个:

代码语言:javascript
复制
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定义(不完全,正如手册页所提到的,前两个可能是一个联合,对我来说就是这样)。

然而,我发现我需要构建的结构看起来更像这样:

代码语言:javascript
复制
struct asm_sigaction {
    void                  (*sa_handler)(int);
    [unsigned?] long      sa_flags;
    void                  (*sa restorer)(void);
    sigset_t              sa_mask; 
};

我通过深入了解我的C代码所做的事情发现了这一点。我找到了我正在制作的同一个syscall的位置,并将字节转储给它们传递给sigaction结构的字节:

代码语言:javascript
复制
(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的那部分就像是一个地址,所以我查了一下:

代码语言:javascript
复制
(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中合适的位置,然后它就起作用了。

这是现在正在运行的NASM,我用一个新的C程序改变了一些东西,我试着让它看起来更像我的NASM程序,并把pause换成了nanosleep

新C程序:

代码语言:javascript
复制
#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 parent\n\0";
const char *childmsg = "from child\n\0";
const char *handlemsg = "in handle\n\0";
const char *forkfailed = "fork failed\n\0";
const char *parentexit = "parent exiting\n\0";
const char *sleepfailed = "sleep failed\n\0";
const char *sleepinterrupted = "sleep interrupted\n\0";

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 (在彼得的帮助下,希望它看起来更好,功能也更好)

代码语言:javascript
复制
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
票数 1
EN

Stack Overflow用户

发布于 2016-06-29 04:29:09

信号处理程序是正常的功能。带ret返回,而不是iret返回。(从那以后你修改了你的问题来解决这个问题,所以我想你还有其他的问题)。

看看gcc是如何在handler()上编译戈德波特编译器浏览器的。

代码语言:javascript
复制
static void handle(int sig) {
    int status;
    wait(&status);
}
代码语言:javascript
复制
    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)的内联调用并不难。

注意,手册页说wait4已经过时,新程序应该使用waitid。但是,如果您不需要更多的功能,wait4就可以了。glibc在wait(3) Linux系统调用的基础上实现wait4,而不是waitid。如果使用wait4有什么问题,glibc将直接使用waitid

代码语言:javascript
复制
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的返回值,请执行以下操作:

代码语言:javascript
复制
    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中。如果您想在堆栈上分配它,而不是静态地分配它,您应该像gcc 5.3在您的rep stosq中所做的那样,用main()对它进行零化。(它插入bzero,就像你在“上帝”上看到的

顺便说一句,你的NASM结构在问题中的填充物在错误的地方。也许值得注意的是你未来的冒险,尽管事实证明它不是这个问题答案的一部分。(在定义了NASM结构语法之后,您的代码甚至没有使用它。)

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

代码语言:javascript
复制
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结构在错误的地方有填充物:

代码语言:javascript
复制
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
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38087981

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档