首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >汇编学习(9), 命令行参数,C与汇编

汇编学习(9), 命令行参数,C与汇编

作者头像
一只小虾米
发布于 2022-12-19 00:33:46
发布于 2022-12-19 00:33:46
73000
代码可运行
举报
文章被收录于专栏:Android点滴分享Android点滴分享
运行总次数:0
代码可运行

本篇介绍

本篇介绍下汇编如何支持命令行函数,以及C如何调用汇编。

命令行参数

看一个访问命令行参数的例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
; cmdline.asm
section .data
    NL    db    10,0
    msg     db "The command and arguments: ",10,0
section .bss
section .text
    global main
main:
push rbp
mov rbp,rsp
    mov r12, rdi ;rdi contains number of arguments
    mov r13, rsi ;rsi contains the address to the array of arguments

printArguments:
    mov rdi, msg
    call printString
    mov rbx, 0 
printLoop:
    mov rdi, qword [r13+rbx*8] 
    call printString
    mov rdi, NL 
    call printString
    inc rbx
    cmp rbx, r12 
    jl printLoop
leave
ret


global printString
printString:
    push rbx
    push rax
    push r12
; Count characters 
    mov r12, rdi
    mov rdx, 0 
strLoop:
    cmp byte [r12], 0
    je strDone
    inc rdx                 ;length in rdx
    inc r12
    jmp strLoop
strDone:
    cmp rdx, 0              ; no string (0 length)
    je prtDone
    mov rsi,rdi
    mov rax, 1 
    mov rdi, 1
    syscall
prtDone:
    pop r12
    pop rax
    pop rbx
    ret
输出结果;
./cmdline 1 2 3
The command and arguments: 
./cmdline
1
2
3

命令行参数个数会保存到rdi中,rsi会记录包含参数地址的数组地址。 接下来也可以debug确认下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
gdb --args ./cmdline 1 2 hello
b main

(gdb) info registers rsi
rsi            0x7fffffffde88      140737488346760
(gdb) info registers rdi 
rdi            0x4                 4
(gdb) x /4xg 0x7fffffffde88
0x7fffffffde88: 0x00007fffffffe1f3  0x00007fffffffe252
0x7fffffffde98: 0x00007fffffffe254  0x00007fffffffe256

(gdb) x /s 0x00007fffffffe252
0x7fffffffe252: "1"
(gdb) x s 0x00007fffffffe254
No symbol "s" in current context.
(gdb) x /s 0x00007fffffffe254
0x7fffffffe254: "2"
(gdb) x /s 0x00007fffffffe256
0x7fffffffe256: "hello"

用 C 访问汇编

接下来写一个例子,用C访问汇编实现的函数,extern 声明的函数由汇编实现,C代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
#include <string.h>

extern int rsurface(int, int);
extern int rcircum(int, int);
extern  double csurface( double);
extern  double ccircum( double);
extern void sreverse(char *, int );
extern void adouble(double [], int );
extern double asum(double [], int );

int main()
{

    char rstring[64];
    int side1, side2, r_area, r_circum;
    double radius,c_area, c_circum;
    double darray[] = {70.0, 83.2, 91.5, 72.1, 55.5};
    long int len;
    double sum;

// call an assembly function with int arguments
    printf("Compute area and circumference of a rectangle\n");
    printf("Enter the length of one side : \n");
    scanf("%d", &side1 );  
    printf("Enter the length of the other side : \n");
    scanf("%d", &side2 ); 
 
    r_area = rsurface(side1, side2);
    r_circum = rcircum(side1, side2);

    printf("The area of the rectangle = %d\n", r_area);
    printf("The circumference of the rectangle = %d\n\n",
 r_circum);

// call an assembly function with double (float) argument
    printf("Compute area and circumference of a circle\n");
    printf("Enter the radius : \n");
    scanf("%lf", &radius);  
 
    c_area = csurface(radius);
    c_circum = ccircum(radius);
    printf("The area of the circle = %lf\n", c_area);
        printf("The circumference of the circle = %lf\n\n", c_circum);

// call an assembly function with string argument
    printf("Reverse a string\n");
    printf("Enter the string : \n");
    scanf("%s", rstring);
    printf("The string is = %s\n", rstring);
    sreverse(rstring,strlen(rstring));
    printf("The reversed string is = %s\n\n", rstring);

// call an assembly function with array argument
    printf("Some array manipulations\n");
    len = sizeof (darray) / sizeof (double);

    printf("The array has %lu elements\n",len);
    printf("The elements of the array are: \n");
    for (int i=0;i<len;i++){
        printf("Element %d = %lf\n",i, darray[i]);
    }
    sum = asum(darray,len);
    printf("The sum of the elements of this array = %lf\n", sum);
    adouble(darray,len);
    printf("The elements of the doubled array are: \n");
    for (int i=0;i<len;i++){
        printf("Element %d = %lf\n",i, darray[i]);
    }
    sum = asum(darray,len);
    printf("The sum of the elements of this doubled array = %lf\n", sum);
    return 0;
}

对应的汇编如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
; rect.asm
section .data                                       
section .bss                            
section .text

global rsurface

rsurface:
    section .text
        mov rax, rdi    
        imul    rsi
        ret                                 
global rcircum

rcircum:    
    section .text   
        mov rax, rdi
        add rax, rsi
        imul    rax, 2
        ret

; circle.asm
section .data                           
    pi  dq  3.141592654         

section .bss                            
section .text

global csurface
csurface:
    section .text   
        movsd   xmm1, qword [pi]
        mulsd   xmm0,xmm0       ;radius in xmm0
        mulsd   xmm0, xmm1
        ret
                                    
global ccircum
ccircum:
    section .text   
        movsd   xmm1, qword [pi]
        addsd   xmm0,xmm0       ;radius in xmm0
        mulsd   xmm0, xmm1
        ret

; sreverse.asm
section .data                           
section .bss                            
section .text

global sreverse
sreverse:

pushing:

        mov rcx, rsi
        mov rbx, rdi
        mov r12, 0
    pushLoop:
        mov rax, qword [rbx+r12]
                push rax
        inc r12
        loop pushLoop

popping:
        mov rcx, rsi
        mov rbx, rdi
        mov r12, 0
    popLoop:
        pop rax
        mov byte [rbx+r12], al 
        inc r12
        loop popLoop

exit:       mov rax, rdi
        ret

; asum.asm
section .data                           
section .bss                            
section .text

global asum
asum:
    section .text   
;calculate the sum
        mov rcx, rsi    ;array length
        mov rbx, rdi    ;address of array
        mov r12, 0
        movsd   xmm0, qword [rbx+r12*8]
        dec rcx  ; one loop less, first element already in xmm0
    sloop:  
        inc r12
        addsd   xmm0, qword [rbx+r12*8]
        loop sloop
ret     ; return sum in xmm0

; adouble.asm
section .data                           
section .bss                            
section .text

global adouble
adouble:
    section .text   
;double the elements
        mov rcx, rsi    ;array length
        mov rbx, rdi    ;address of array
        mov r12, 0
    aloop:
        movsd   xmm0, qword [rbx+r12*8] ;take an element from array
        addsd   xmm0,xmm0               ; double it
        movsd   qword [rbx+r12*8], xmm0 ;move it to array
        inc r12
        loop aloop
ret

输出如下:
Compute area and circumference of a rectangle
Enter the length of one side : 
10
Enter the length of the other side : 
20
The area of the rectangle = 200
The circumference of the rectangle = 60

Compute area and circumference of a circle
Enter the radius : 
5
The area of the circle = 78.539816
The circumference of the circle = 31.415927

Reverse a string
Enter the string : 
hello
The string is = hello
The reversed string is = olleh

Some array manipulations
The array has 5 elements
The elements of the array are: 
Element 0 = 70.000000
Element 1 = 83.200000
Element 2 = 91.500000
Element 3 = 72.100000
Element 4 = 55.500000
The sum of the elements of this array = 372.300000
The elements of the doubled array are: 
Element 0 = 140.000000
Element 1 = 166.400000
Element 2 = 183.000000
Element 3 = 144.200000
Element 4 = 111.000000
The sum of the elements of this doubled array = 744.600000

关键信息就是通过C调用汇编,还是按照调用约定就行,返回值用rax或xmm0传递。

内联汇编

首先除非必要,尽量不用内联汇编。 内联汇编有2种,Basic Inline 和 Extended Inline。

Basic Inline

首先看一个数字运算的例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

int x=11,y=12,sum,prod;
int subtract(void);
void multiply(void);

int main(void)
{
    printf("The numbers are %d and %d\n",x,y);
    __asm__(
        ".intel_syntax noprefix;"   
        "mov rax,x;"
        "add rax,y;"
        "mov sum,rax"
        );
    printf("The sum is %d.\n",sum);
    printf("The difference is %d.\n",subtract());
    multiply();
    printf("The product is %d.\n",prod);    
    
}

int subtract(void)
{
    __asm__(
        ".intel_syntax noprefix;"   
        "mov rax,x;"
        "sub rax,y"         // return value in rax
        );
}

void multiply(void)
{
    __asm__(
        ".intel_syntax noprefix;"   
        "mov rax,x;"
        "imul rax,y;"
        "mov prod,rax"          //no return value, result in prod
        );
}

结果如下:
$ ./inline1 
The numbers are 11 and 12
The sum is 23.
The difference is -1.
The product is 132.

Basic 内联需要注意的点如下:

  • 第一行需要标明汇编类型,是ATT还是Intel,这样汇编器才会正确解析
  • 汇编用到的参数必须定义成全局变量
  • 如果使用的寄存器保存有关键数据,会被覆盖,这时候后果不确定,关于这一点通过 Extended Inline 可以解决。

Extended Inline

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// inline2.c

#include <stdio.h>
    int a=12;   // global variables
    int b=13;
    int bsum;

int main(void)
{

printf("The global variables are %d and %d\n",a,b);
__asm__(
    ".intel_syntax noprefix\n"
    "mov rax,a \n"
    "add rax,b \n"
    "mov bsum,rax \n"
    :::"rax"
    );

     printf("The extended inline sum of global variables is %d.\n\n", bsum);

int x=14,y=16, esum, eproduct, edif;  // local variables

printf("The local variables are %d and %d\n",x,y);

__asm__( 
    ".intel_syntax noprefix;"
    "mov rax,rdx;"
    "add rax,rcx;"
    :"=a"(esum)
    :"d"(x), "c"(y)
    );
     printf("The extended inline sum is %d.\n", esum);

__asm__(
    ".intel_syntax noprefix;"
    "mov rbx,rdx;"
    "imul rbx,rcx;"
    "mov rax,rbx;"
    :"=a"(eproduct)
    :"d"(x), "c"(y)
    :"rbx"
    );
     printf("The extended inline product is %d.\n", eproduct);

__asm__(
    ".intel_syntax noprefix;"
    "mov rax,rdx;"
    "sub rax,rcx;"
    :"=a"(edif)
    :"d"(x), "c"(y)
    );
     printf("The extended inline asm difference is %d.\n", edif);

}

结果如下:
$ ./inline2 
The global variables are 12 and 13
The extended inline sum of global variables is 25.

The local variables are 14 and 16
The extended inline sum is 30.
The extended inline product is 224.
The extended inline asm difference is -2.

模式如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
asm (
assembler code
: output operands
*//* optional
: input operands
*//* optional
: list of clobbered registers
*//* optional
);

可选部分需要按照寄存器约束,常用部分映射关系如下:

image.png

这儿明显的差异如下:

  • 通过寄存器显示传递参数
  • 需要声明可能会修改的寄存器,这样系统就会帮恢复原始值,避免异常
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-12-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
汇编学习(3),ddd以及跳转循环
可以发现ddd 就是带有GUI的gdb,先在main上打个断点,点击run,这时候就会停到main上。
一只小虾米
2022/12/02
4370
汇编学习(3),ddd以及跳转循环
汇编学习(12), SIMD之AVX(已完结)
AVX(Advanced Vector Extensions)用的是ymm寄存器,每个256字节。 先看一个数据操运算的例子:
一只小虾米
2022/12/28
5980
linux、memory、memcmp 几种实现和性能对比
前言 memcmp是最基本的库函数了。下文选择几版代码,来对比分析性能。 分析 1.kernel memcmp 代码选自linux4.4/lib/string.c int memcmp(const v
皮振伟
2017/07/19
6.2K1
汇编学习(4), 整数,栈,浮点
可以用于有符号,也可以用于无符号,第二个操作数会加到第一个操作数上。对于有符号数,如果第一个操作数放不下结果,那么CF 标记会置位,对于无符号数,OF标记会置位,如果结果是0,ZF标记会置位,如果结果是负数,那么SF标记会置位。 sub 和add类似,无需重复。
一只小虾米
2022/12/07
3610
汇编学习(1),汇编之helloworld
makefile可以这样看,目标hello依赖hello.o, 而hello.o又依赖hello.asm, 如果hello.asm的修改时间大于hello.o,那么hello.o下一行的命令就需要执行。
一只小虾米
2022/11/28
9950
汇编学习(5),函数,栈帧
这儿又出现了一个leave指令,leave就等同于 mov rsp,rbp,pop rbp。
一只小虾米
2022/12/07
5180
汇编学习(5),函数,栈帧
汇编学习(8) 宏,IO
首先宏并不是汇编支持的,而是nasm 汇编器支持的,这个也容易想到,汇编本身是一套指令,而宏就是将若干指令替换成一个符号,在编译的时候再展开到代码中,这完全是编译层面的能力。 接下来看一个代码例子,nasm汇编的使用;
一只小虾米
2022/12/13
5750
汇编学习(10) 字符串
C语言中定义字符串是会以"\0"结束,汇编中不会这样,只要是一块连续的内存,都可以认为是字符串。 下面是一段操作字符串的代码:
一只小虾米
2022/12/19
4800
【连载】两百行Rust代码解析绿色线程原理(五)附录:支持 Windows
我们的示例适用于 OSX、Linux 和 Windows,但我之前曾指出,虽然这个实现在 Windows 上可以运行,但并不正确。因为我一直致力于使这个实现在所有三个平台上都能正确运行,所以我将在本章中介绍我们还需要做的事情。
MikeLoveRust
2020/02/12
7090
【参赛经验分享】实现一个世界上最小的程序来输出自身的MD5 388解法分享
这题整体思路其实大家应该都很明白了。这里主要是列举一些优化点。elf header相关的做的比较挫,求其他大神思路。
王沛文
2021/08/30
6770
bthread源码剖析(三): 汇编语言实现的上下文切换
上回书说道,TaskGroup的run_main_task()有三大关键函数,剩余一个sched_to()没有展开详谈。那在今天的sched_to()源码探秘之旅开始之前呢,首先高能预警,本文会涉及到汇编语言,所以请大家坐稳扶好!
果冻虾仁
2021/12/08
1K0
汇编学习(7), Bit 操作
这儿再回顾下leave 和ret的区别: leave 本质上就是epilogue 指令,恢复rsp指针,从栈上弹出rbp指针。 ret本质上也是弹栈,将栈上保存的返回地址弹出并赋值给rip指针,这样就可以接着执行了。
一只小虾米
2022/12/11
3540
汇编学习(6), 外部函数,调用约定
在前面已经多次见过使用printf了,这次我们也可以自己写一些外部函数,下面是一个例子: 首先定义2个外部函数,分别是c_area和c_circum。
一只小虾米
2022/12/11
8180
汇编学习(6), 外部函数,调用约定
挂钩 NtCreateThreadEx 导致 0xC00000F2 问题
近期在研究和开发基于虚拟化的虚拟 HOOK 技术。在 Windows 7 x64 环境开发实测期间,发现针对 NtCreateThreadEx 函数的 HOOK 存在问题:该函数大部分情况下变得只返回 0xC00000F2 (STATUS_INVALID_PARAMETER_4) 第 4 个参数无效的状态码。这导致系统出现很多问题,大部分的新线程都无法成功创建。为了解决这个问题,在这篇文章中对问题进行追溯,查找到底是哪里导致的。
稻草小刀
2022/12/12
8360
挂钩 NtCreateThreadEx 导致 0xC00000F2 问题
分享如何阅读Go源码
以我个人理解,Go源码主要分为两部分,一部分是官方提供的标准库,一部分是Go语言的底层实现,Go语言的所有源码/标准库/编译器都在src目录下:https://github.com/golang/go/tree/master/src,想看什么库的源码任君选择;
Golang梦工厂
2022/07/11
8390
分享如何阅读Go源码
每个程序员都应该了解的硬件知识
在追求高效代码的路上,我们不可避免地会遇到代码的性能瓶颈。为了了解、解释一段代码为什么低效,并尝试改进低效的代码,我们总是要了解硬件的工作原理。于是,我们可能会尝试搜索有关某个架构的介绍、一些优化指南或者阅读一些计算机科学的教科书(如:计算机组成原理)。但以上的内容可能都太过繁琐、细节太多,在阅读的过程中,我们可能会迷失在纷繁的细节中,没法很好地将知识运用到实践中。
腾讯技术工程官方号
2024/04/03
5560
每个程序员都应该了解的硬件知识
x86_64汇编调试程序初步
掌握此基础,就可以用来修改无源代码的程序等,比如希望jstatd在指定的端口上监听,而不是一个值为0的随机端口号,请参见《防火墙内JVisualVM连接jstatd解决方案》。
一见
2018/12/24
7830
用纯汇编来写strlen一定比C语言写的快吗?
事情是这样的,我在写操作系统,看到田宇大佬写的代码里面,strlen也是用汇编来写的,我很不解,这个不是可以用C来实现吗?难不成纯汇编更快?于是我就写了一个小程序来做实验。看看这两者的速度。
灯珑LoGin
2022/10/31
6750
SEH分析笔记(X64篇)
SEH分析笔记(X64篇) v1.0.0 boxcounter 历史: v1.0.0, 2011-11-4:最初版本。 [不介意转载,但请注明出处 www.boxcounter.com 附件里有本文的原始稿,一样的内容,更好的高亮和排版。 本文的部分代码可能会因为论坛的自动换行变得很乱,需要的朋友手动复制到自己的代码编辑器就可以正常显示了] 在之前的《SEH分析笔记(X86篇)》中,我借助 wrk1.2 介绍了 x86 下 windows 系统内核中的 SEH 实现。这次我们来看看 x64 位 windows 系统内核中 SEH 的实现。 本文需要大家熟悉 x64 位系统的一些特性,比如调用约定、Prolog 和 Epilog。可以通过这几篇文章熟悉一下: Overview of x64 Calling Conventions, MSDN The history of calling conventions, part 5: amd64 , The Old New Thing Everything You Need To Know To Start Programming 64-Bit Windows Systems, Matt Pietrek 首先回顾一下前一篇文章。 在 x86 windows 中,函数通过以下几个步骤来参与 SEH : 1. 在自身的栈空间中分配并初始化一个 EXCEPTION_REGISTRATION(_RECORD) 结构体。 2. 将该 EXCEPTION_REGISTRATION(_RECORD) 挂入当前线程的异常链表。 当某函数触发异常时,系统首先会通过调用 KiDispatchException 来给内核调试器一个机会,如果内核调试器没有处理该异常,则该机会被转给 RtlDispatchException,这个函数就开始分发该异常。分发过程为: 从当前线程的异常链表头开始遍历,对于每一个 SEH 注册信息(即 EXCEPTION_REGISTRATION(_RECORD)),调用其 Handler。根据 Handler 的返回值做相应的后续处理: 1. 返回 ExceptionContinueExecution,表示 Handler 已经修复了异常触发点,从异常触发点继续执行。 2. 返回 ExceptionContinueSearch,表示该 Handler 没有处理该异常,继续遍历异常链表。 3. Handler 没有修复异常触发点,但是却能处理该异常(某个 __except 过滤代码返回 EXCEPTION_EXECUTE_HANDLER)。这种情况下,处理完该异常后就从异常解决代码(__except 代码块)继续执行,Handler 不会返回。 以上是简略的 x86 SEH 流程,其中省略了很多细节,比如展开、错误处理、ExceptionNestedException 和 ExceptionCollidedUnwind 等等。 之所以在这里重温这个流程,是因为 x64 中 SEH 的流程总体思路也是如此,只是细节上做了一些修改。但这并不表示熟悉 x86 SEH 就能很轻松的掌握 x64 SEH。 本文分为四个部分:“异常注册”、“异常分发”、“展开、解决”和“ExceptionNestedException 和 ExceptionCollidedUnwind”。依然以 MSC 的增强版为分析对象。分析环境为:WDK 7600.16385.1,内置的 cl 的版本是15.00.30729.207,link 的版本是9.00.30729.207,测试虚拟机系统为 amd64 WinXP + wrk1.2。 在讲述之前,需要先定义几个名词,以简化后续的讲述。 RVA —— 熟悉 PE 格式的朋友都懂的,表示某个绝对地址相对于所在模块的基地址的偏移。 EXCEPT_POINT —— 异常触发点。 EXCEPT_FILTER —— __except 小括号内的异常过滤代码。 EXCEPT_HANDLER —— __except 大括号内的异常解决代码。 FINALLY_HANDLER —— __finally 大括号内的代码。 以下面的伪码为例,
战神伽罗
2019/07/24
1.7K0
用Rust实现Brainfuck的JIT编译器
希望读者们都可以理解上述 C 代码的作用。但是,此代码在底层如何工作?我认为并非所有人都能回答这个问题,我也是。我可以用Haskell,Erlang,Go 等高级编程语言编写代码,但是在它们编译后我并不知道它在底层是如何工作的。因此,我决定采取一些更深入的步骤,进行记录,并描述我对此的学习过程。希望这个过程不仅仅只是对我来说很有趣。让我们开始吧。
端碗吹水
2022/06/06
1K0
相关推荐
汇编学习(3),ddd以及跳转循环
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验