我目前正在通过反汇编C程序并试图了解它们的作用来进行汇编阅读.
我被困在一个简单的问题:一个简单的你好世界计划.
#include <stdio.h>
#include <stdlib.h>
int main() {
printf("Hello, world!");
return(0);
}
Run Code Online (Sandbox Code Playgroud)
当我拆卸主要时:
(gdb) disassemble main
Dump of assembler code for function main:
0x0000000000400526 <+0>: push rbp
0x0000000000400527 <+1>: mov rbp,rsp
0x000000000040052a <+4>: mov edi,0x4005c4
0x000000000040052f <+9>: mov eax,0x0
0x0000000000400534 <+14>: call 0x400400 <printf@plt>
0x0000000000400539 <+19>: mov eax,0x0
0x000000000040053e <+24>: pop rbp
0x000000000040053f <+25>: ret
Run Code Online (Sandbox Code Playgroud)
我理解前两行:基本指针保存在堆栈上(通过push rbp,这会导致堆栈指针的值减少8,因为它已经"增长")并且堆栈指针的值被保存在基指针中(这样,参数和局部变量可以分别通过正偏移和负偏移轻松到达,而堆栈可以保持"增长").
第三行提出了第一个问题:为什么0x4005c4("Hello,World!"字符串的地址)在edi寄存器中移动而不是在堆栈中移动?printf函数不应该将该字符串的地址作为参数吗?据我所知,函数从堆栈中获取参数(但在这里,看起来参数放在该寄存器中:edi)
在StackOverflow上的另一篇文章中,我读到"printf @ ptl"就像一个调用真正的printf函数的存根函数.我试图反汇编这个功能,但它变得更加混乱:
(gdb) disassemble printf
Dump of assembler code for function __printf:
0x00007ffff7a637b0 <+0>: sub rsp,0xd8
0x00007ffff7a637b7 <+7>: test al,al …Run Code Online (Sandbox Code Playgroud) 我正在开发一个带有 C++ 函数的 R 包。我使用 Rcpp。编写和测试时一切正常。但是,当我完成并循环运行它时,我偶尔会收到以下警告和错误:

警告:“.Call”中的堆栈不平衡,78 然后是 77
警告:'{' 中的堆栈不平衡,75 然后是 74
警告:“.Call”中的堆栈不平衡,78 然后是 79
警告:'{' 中的堆栈不平衡,75 然后是 76
警告:“.Call”中的堆栈不平衡,78 然后是 77
警告:'{' 中的堆栈不平衡,75 然后是 74
所以我用谷歌搜索堆栈不平衡,我发现:
本文指出堆栈不平衡通常是由语言之间调用约定的差异(不匹配)引起的。据我所知,callng约定是调用者函数删除局部变量还是被调用者函数删除的约定。
我发现了这个:
http://www.stats.uwo.ca/faculty/murdoch/software/compilingDLLs/
它说 R 正在使用cdecl而 C++ 使用stdcall.
“如果您的函数使用了
stdcall而不是cdecl,则返回时可能会发生崩溃,因为您的参数将在它们已经消失后从堆栈中删除。”
如果我在声明中犯了任何错误,请告诉我。
这是我的 R 代码调用的 C++ 函数:
//[[Rcpp::export]]
int compute(SEXP p_in, Rcpp::NumericVector a) {
Rcpp::XPtr<emcdf> p(p_in);
p->cdf(a);
return p->getResult();
}
//p_in is an external pointer(pointer to an emcdf object in C++)
Run Code Online (Sandbox Code Playgroud)
所以,我的问题是,我该如何解决这个问题?
我感谢您的帮助。
我已经做了这个代码:
global strlen
; int strlen(const char *string);
strlen:
xor rcx, rcx
retry:
cmp byte [rdi + rcx], 0
je result
inc rcx
jmp retry
result:
mov rax, rcx
ret
Run Code Online (Sandbox Code Playgroud)
这就是我测试它的方式:
#include <stdio.h>
int main(int argc, char **argv)
{
char* bob = argv[1];
printf("%i\n", strlen(bob));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这是一个有效的 strlen,这里没问题,但我注意到我可以rdi在重试块的第一行切换arax而不改变任何东西,我不知道这是否是正常行为。我应该保留哪些值?
C中的函数是
void f(int* out, int* in, int nbElements){
// do stuff
}
Run Code Online (Sandbox Code Playgroud)
由于int nbElements是第一个被压入堆栈in并且out具有可变大小,我如何访问 的值nbElements?据我了解,堆栈看起来像这样:
esp
ebp
return address # -4(%ebp)
1st element of int* out # -8(%ebp)
1st element of int* in # (%ebp - 8 - 4*nbElements)
nbElements # not sure how I can access the value of this
Run Code Online (Sandbox Code Playgroud)
那么如何在nbElements不知道其地址的情况下访问的值呢?
我们知道,根据 x86-64 约定,寄存器%rbx、%rbp和%r12–%r15被归类为被调用者保存的寄存器。While%r10和%r111是调用者保存的寄存器。但是当我在大多数情况下编译 C 代码时,例如函数P调用Q,我看到以下函数的汇编代码Q:
Q:
push %rbx
movq %rdx, %rbx
...
popq %rbx
ret
Run Code Online (Sandbox Code Playgroud)
我们知道,由于%rbx是一个被调用者保存的寄存器,我们必须将它存储在堆栈中,并在P以后为调用者恢复它。
但它不会更简洁并通过使用调用者保存的寄存器来保存堆栈操作%r10:
Q:
movq %rdx, %r10
...
ret
Run Code Online (Sandbox Code Playgroud)
所以被调用者不需要担心保存和恢复调用者的寄存器,因为调用者在调用被调用者之前已经把它推到了堆栈?
我用 C 语言编写了一个非常简单的函数,它使用strlen()from<string.h>返回变量的长度char*:
int length(char *str) {
return strlen(str);
}
Run Code Online (Sandbox Code Playgroud)
以下是相应的 x86_64 程序集objdump -M intel -d a.out:
00000000000011a8 <length>:
11a8: f3 0f 1e fa endbr64
11ac: 55 push rbp
11ad: 48 89 e5 mov rbp,rsp
11b0: 48 83 ec 10 sub rsp,0x10
11b4: 48 89 7d f8 mov QWORD PTR [rbp-0x8],rdi
11b8: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8]
11bc: 48 89 c7 mov rdi,rax
11bf: e8 ac fe ff ff …Run Code Online (Sandbox Code Playgroud) 我编写了一个最小函数来测试是否可以调用/链接 C 和 x86_64 汇编代码。
这是我的main.c
#include <stdio.h>
extern int test(int);
int main(int argc, char* argv[])
{
int a = 10;
int b = test(a);
printf("b=%d\n", b);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这是我的test.asm
section .text
global test
test:
mov ebx,2
add eax,ebx
ret
Run Code Online (Sandbox Code Playgroud)
我使用这个脚本构建了一个可执行文件
#!/usr/bin/env bash
nasm -f elf64 test.asm -o test.o
gcc -c main.c -o main.o
gcc main.o test.o -o a.out
Run Code Online (Sandbox Code Playgroud)
我写的时候test.asm并没有任何真正的线索我在做什么。然后我离开并做了一些阅读,现在我不明白我的代码是如何工作的,因为我说服自己它不应该这样。
以下是我认为这不起作用的原因列表:
eaxand传递的ebx。我认为这是不对的。ret可能希望从某个地方获取返回地址。我相当确定我没有提供这个。我所编写的内容产生正确的输出完全是侥幸吗?
我对此完全陌生。虽然我顺便听说过一些 …
汇编:
.intel_syntax noprefix
.global Foo
Foo:
mov ax, 146
ret
Run Code Online (Sandbox Code Playgroud)
主要.c:
#include <stdio.h>
extern int Foo(void);
int main(int argc, char** args){
printf("Asm returned %d\n", Foo());
return 0;
}
Run Code Online (Sandbox Code Playgroud)
现在我编译并链接:
(compiler name) -c asm.s -o asm.o
(compiler name) asm.o main.c -o main
./main
Run Code Online (Sandbox Code Playgroud)
我正在使用 Windows 和 LLVM 的 x64 windows 二进制文件。
GCC 打印 146(如预期),但 clang 生成的代码打印随机值,为什么?在 Windows 上使用 gdb 调试 clang 二进制文件显然存在一些问题,因此我无法提供任何 gdb 日志。GCC 二进制文件正在执行我期望的操作,但不是 Clang。
如何在下面的例子中getRequestDispatcher("xxx")调用getServletContext()?这样的调用程序如何工作?请给我一个关于这个背景的清晰图片.
RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/index.jsp");
dispatcher.include(request, response);
Run Code Online (Sandbox Code Playgroud)