汇编:如何return一个指针?

Assembly: How to return a pointer?

函数 inventory 获取设备指针数组并调用 evaluate 以找出变化是什么。然后库存函数 returns 一个具有最高变化的指针。

unsigned short evaluate(Struct Device *thing);

struct Device *inventory(struct Device *things[], int count);

device.h:

struct Device{
   char name[20];
   short adjustments[8];
   short avg;
}

main.c:

#include <stdio.h>
#include <stdlib.h>

#include "device.h"

/* from device.h, shown here to make it easier to code
struct Device
{   
    char name[20];
    short adjustments[8];
    short avg;
};
*/

struct Device things[] =
{
    { "Museum Quality",{ 0, 1, 0, -1, 0, -1, 0, 1}, 1 },
    { "Fell off truck",
        { 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff},
    2 },

    /* the casts are there since C constants are ints and you can't make
     * them shorts and negative numbers look like overflow */
    { "Fell down stairs",
    { 0x7ff0, (unsigned short)0x800f, 0x7ffe, (unsigned short)0x8001, 
    0x7ffd, (unsigned short)0x8002, 0x7fff, 0x7f00}, 
    3 },

    { "On left side",{ 10, 11, 10, 12, 10, 13, 10, 14}, 4 },
    { "On right side",{ -300, -321, -320, -332, -320, -313, -310, -314}, 5 }
};


/*
struct Device *inventory(struct Device *things[], int count);
*/

int main()
{

    struct Device *pointers[1 + sizeof(things) / sizeof(things[0])] 
        = {NULL};
    const int count =  sizeof(things) / sizeof(things[0]);

    struct Device *worst = NULL;

    int i;


    for (i=0; i< count; i++)
    {
        printf("Loading %s into pointers\n", things[i].name);
        pointers[i] = &things[i];
    }
    pointers[i] = NULL;

    worst = inventory(pointers, count);

    printf("main: The worst is %s\n", worst->name);


    return(EXIT_SUCCESS);
}

calibrate.s: # 计算平均值和returns变异性

    movq %r11, %rax
    sarq , %rax
    movl %eax, 36(%rdi)
    subq %rcx, %rdx
    movq %rdx, %rax

evaluate.s: # 调用校准和 returns 可变性

        call calibrate

inventory.s:

rdi 是保存设备指针数组的参数,#rbx 是 rdi 的副本,因此它存储设备指针数组,#r12 存储有效指针的计数,#r14 存储最大变化,#r15存储变化最大的设备指针。

        movq %rdi, %r15         #r15 stores pointer with most variable

   loop: 
        movq (%rbx,%r12,8), %rdi
        call evaluate
        
        cmpq %r14, %rax
        jle skipmax
        movq %rax, %r14
        movq (%rbx, %r12,8), %r15
 skipmax:
        decq %r12
        cmpq [=16=], %r12
        jge loop

        movq %r13, %rsi
        movq $.LC0, %rdi
        movq [=16=], %rax
        call printf
            
        movq %r15, %rdx
        movq %r14, %rsi
        movq $.LC1, %rdi
        movq [=16=], %rax
        call printf

        movq %r15, %rax
                

I 运行 程序,结果是:r14 正确获得最高变化,但 LC0 打印正确的最高变化 (r14) 但空名称 (r15),以及试图打印的 C 程序rax中存储的名字也是空的,print语句打印:

的最大差异为 65534

相反,它应该打印:

Fell down stairs 的最大变化是 65534

正如您从上面看到的那样,名称没有被打印出来。

经过进一步排查,我发现r15的指针是正确的,它的adjustment和avg值也是正确的,只是name是空字符串,知道为什么吗?也许是因为名称是一个字符数组,并且没有被正确复制?

谢谢!

错误

错误是 movl %eax, 36(%rdi)calibrate.s 的第 38 行。这显然应该写入相关 Deviceavg 成员,但它是一个 32 位存储,而 Device::avg 是一个 16 位 short。所以应该是movw %ax, 36(%rdi).

我是如何用 gdb 找到它的

希望这会提供一些关于 gdb 可以做什么以及如何有效使用它的信息。

我在inventory中的第二个printf设置了一个断点,此时我x/s $rdx查看字符串%rdx指向什么:

(gdb) x/s $rdx
0x4040ac <things+76>:   ""

好的,所以它是一个空字符串,但它在 things 内的偏移量 76 处,您可以检查它是 things[2].name 的地址。这不应该是一个空字符串,所以让我们看看 things.

发生了什么
(gdb) p things
 = {{name = "Museum Quality[=11=]0[=11=]0[=11=]0[=11=]0[=11=]0", adjustments = {0, 1, 0, -1, 0, -1, 0, 1}, 
    avg = 0}, {name = "[=11=]0[=11=]0ll off truck[=11=]0[=11=]0[=11=]0[=11=]0[=11=]0", adjustments = {32767, 32767, 32767, 
      32767, 32767, 32767, 32767, 32767}, avg = 32767}, {name = "[=11=]0[=11=]0ll down stairs[=11=]0[=11=]0[=11=]0", 
    adjustments = {32752, -32753, 32766, -32767, 32765, -32766, 32767, 32512}, avg = 8159}, {
    name = "[=11=]0[=11=]0 left side[=11=]0[=11=]0[=11=]0[=11=]0[=11=]0[=11=]0[=11=]0", adjustments = {10, 11, 10, 12, 10, 13, 
      10, 14}, avg = 11}, {name = "[=11=]0[=11=]0 right side[=11=]0[=11=]0[=11=]0[=11=]0[=11=]0[=11=]0", adjustments = {-300, 
      -321, -320, -332, -320, -313, -310, -314}, avg = -317}}

嗯,things[2].name 的前两个字节现在为空,其他几个字节也为空。这不是它们的初始化方式,根本不应该修改名称,那是怎么发生的呢?一个观察点会告诉我们哪个指令是错误的。

(gdb) watch things[1].name[0]
Hardware watchpoint 3: things[1].name[0]
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
...
Hardware watchpoint 3: things[1].name[0]

Old value = 70 'F'
New value = 0 '[=12=]0'
calibrate () at calibrate.s:39
39          subq %rcx, %rdx

请注意,这是 触发观察点的指令之后的指令。所以我们回到第 38 行,想一想,我们到了。