从 64 位汇编调用 C 函数

2023-12-05

在 ubuntu 16.04 上

$ cat hola.asm

    extern puts
    global main

    section .text
main:
    mov rdi,message
    call puts
    ret

message:
    db  "Hola",0
$ nasm -f elf64 hola.asm  
$ gcc hola.o

/usr/bin/ld: Hola.o: 针对符号重定位 R_X86_64_PC32 创建共享对象时不能使用“puts@@GLIBC_2.2.5”; 使用 -fPIC 重新编译
/usr/bin/ld:最终链接失败:错误值 collect2:错误:ld 返回 1 退出状态

Use:

$gcc -fPIC hola.o -o hola && ./hola
Hola

文档:

-fPIC 如果目标机器支持,则发出与位置无关的代码,适合动态链接并避免对大小的任何限制 全局偏移表。此选项会产生影响 AArch64、m68k、PowerPC 和 SPARC。

位置无关代码需要特殊支持,因此 仅适用于某些机器。当设置该标志时,宏 ”pic" and "PIC" 定义为 2. 位置无关代码 需要特殊支持,因此仅适用于某些 机器。

gcc 的 -static 选项有效:

使用-static完全避免外部调用动态库

$nasm -f elf64 -l hola.lst hola.asm && gcc -m64 -static -o hola hola.o && ./hola
Hola

并且:

$nasm -f elf64 hello.asm && gcc -static -o hola hola.o && ./hola Hola

包括 wrt ..plt 也有效

 global main
    extern puts

    section .text
main:
    mov rdi,message
    call puts wrt ..plt
    ret
message:
    db "Hola", 0




$nasm -f elf64 hola.asm
$gcc -m64 -o hola hola.o && ./hola
Hola

from ..plt描述

..plt 使用 wrt ..plt 引用过程名称会导致链接器为该符号构建过程链接表条目,并且引用给出 PLT 条目的地址。您只能在通常会生成 PC 相对重定位的上下文中使用它(即作为 CALL 或 JMP 的目标),因为 ELF 不包含绝对引用 PLT 条目的重定位类型。


我编写这个程序的目的与 hi.c 程序相同,但没有 c lib 调用。然后按照建议在 hi.c 上使用 -S gcc 选项,然后剖析生成的 hi.s 程序。

$ 猫 hiasm.asm

section .text
    global _start

_start:

    mov     dl, 5
    mov     esi, msg
    xor     di,di
    xor     al,al
    inc     di
    inc     al
    syscall

    xor     rdi,rdi 
    mov al,60
    syscall

msg:    db "Hello"

$ nasm -f elf64 hiasm.asm && ld -m elf_x86_64 hiasm.o -o hiasm && ./hiasm

Hello

$回声$?

所以这很好用

再说一遍,这是简单的 hi.c

$ 猫 hi.c

#include <stdio.h>

int main(void)
{
    puts("Hello");
    return 0;
}

$ gcc -s hi.c && cat hi.s

    .file   "hi.c"
    .section    .rodata
.LC0:
    .string "Hello"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    leaq    .LC0(%rip), %rdi
    call    puts@PLT
    movl    $0, %eax
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Debian 6.3.0-18) 6.3.0 20170516"
    .section    .note.GNU-stack,"",@progbits

$ gcc hi.s -o hi && ./hi

Hello

.s 文件中似乎未引用标签 .LFB0 和 .LFE0 删除这两个文件后仍然按预期工作, 引用“as”汇编器文档:

https://sourceware.org/binutils/docs/as/index.html

局部符号是在汇编器内定义和使用的,但它们是 通常不保存在目标文件中。因此,当 调试。您可以使用“-L”选项(请参阅包含本地符号)来 保留目标文件中的本地符号。

因此,作为一个纯粹的可执行文件,不需要花哨的东西,它们可以被砍掉

所以我摆脱了简单的

接下来函数要调用main,这个没有太大用处,所以我会调用_start

对于 ELF 目标,.size 指令的使用方式如下:

 .size name , expression

该指令设置与符号名称关联的大小。尺寸 以字节为单位是根据可以使用标签的表达式计算的 算术。该指令通常用于设置 函数符号。

不需要函数符号大小,去掉了底部引用 main 的 .size

$猫嗨

file    "hi.c"          ##tells 'as' that we are about to start a new logical file
        .section    .rodata     ##assembles the following code into section '.rodata'
    .LC0:                   ##.LC0, .LFB0, .LFE0 are just local labels; symbols that
                    ##  are guaranteed to be unique over the source code
                    ##  that allow the compiler to use names/simple notation
                    ##  to reference sections of code
                    ##But here, only .LC0 is actually referenced in the code

    .string "Hello"         ##
    .text
    .globl  _start
_start:
    .cfi_startproc          ##used at the beginning of each function that should have an
                    ##entry in .eh_frame. It initializes some internal data
                    ##structures. Don't forget to close by .cfi_endproc
    pushq   %rbp            ##push base pointer onto stack

    .cfi_def_cfa_offset 16      ##modifies a rule for computing CFA. Register remains the
                    ##same, but offset is new. Note that it is the absolute
                    ##offset that will be added to a defined register to
                    ##compute CFA address
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    leaq    .LC0(%rip), %rdi
    call    puts@PLT
    movl    $0, %eax
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc            ##close of .cfi_startproc

    .ident  "GCC: (Debian 6.3.0-18) 6.3.0 20170516"
    .section    .note.GNU-stack,"",@progbits

尝试这样做:

$ gcc -o hi hi.s

/tmp/ccLxG1jh.o: In function `_start':
hi.c:(.text+0x0): multiple definition of `_start'
/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/Scrt1.o:(.text+0x0): first defined here
/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/Scrt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
collect2: error: ld returned 1 exit status

$ ldd hi

linux-vdso.so.1 (0x00007fffb6569000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe7456e7000)
/lib64/ld-linux-x86-64.so.2 (0x000055edc8bc8000)

肯定是使用了libc,这就解释了我们对_start的多重定义 所以我会尝试使用 -nostdlib gcc 选项摆脱 std lib

$ gcc -nostdlib -o hi hi.s

/tmp/ccV5QYaT.o: In function `_start':
hi.c:(.text+0xc): undefined reference to puts'
collect2: error: ld returned 1 exit status

是的,看跌期权仍然需要 C,摆脱看跌期权

.file   "hi.c"          ##tells 'as' that we are about to start a new logical file
.section    .rodata     ##assembles the following code into section '.rodata'
.LC0:                   ##.LC0, .LFB0, .LFE0 are just local labels; symbols that
                    ##  are guaranteed to be unique over the source code
                    ##  that allow the compiler to use names/simple notation
                    ##  to reference sections of code
                ##But here, only .LC0 is actually referenced in the code

.string "Hello"         ##
.text
.globl  _start
_start:
    .cfi_startproc          ##used at the beginning of each function that should have an
                    ##entry in .eh_frame. It initializes some internal data
                    ##structures. Don't forget to close by .cfi_endproc
    pushq   %rbp            ##push base pointer onto stack

.cfi_def_cfa_offset 16      ##modifies a rule for computing CFA. Register remains the
                ##same, but offset is new. Note that it is the absolute
                ##offset that will be added to a defined register to
                ##compute CFA address
.cfi_offset 6, -16
movq    %rsp, %rbp
.cfi_def_cfa_register 6
leaq    .LC0(%rip), %rsi     ##this reg value and others were changed for write call
movq    $1, %rax
movq    $1, %rdi
movq    $5, %rdx
syscall

movl    $0, %eax
popq    %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc            ##close of .cfi_startproc

$ gcc -nostdlib -o hi.s && ./hi

你好,分段错误

有希望

.file   "hi.c"          ##tells 'as' that we are about to start a new logical file
.section    .rodata     ##assembles the following code into section '.rodata'
.LC0:                   ##.LC0, .LFB0, .LFE0 are just local labels; symbols that
                    ##  are guaranteed to be unique over the source code
                    ##  that allow the compiler to use names/simple notation
                    ##  to reference sections of code
                    ##But here, only .LC0 is actually referenced in the code

.string "Hello"         
.text
.globl  _start
_start:
    .cfi_startproc          ##used at the beginning of each function that should have an
                    ##entry in .eh_frame. It initializes some internal data
                    ##structures. Don't forget to close by .cfi_endproc

##deleted the base pointer push and pops from stack, don't need stack

.cfi_def_cfa_offset 16      ##modifies a rule for computing CFA. Register remains the
                ##same, but offset is new. Note that it is the absolute
                ##offset that will be added to a defined register to
                ##compute CFA address
.cfi_offset 6, -16
movq    %rsp, %rbp
.cfi_def_cfa_register 6
leaq    .LC0(%rip), %rsi
movq    $1, %rax
movq    $1, %rdi
movq    $5, %rdx
syscall

xor %rdi,%rdi   
mov $60, %rax
.cfi_def_cfa 7, 8
syscall
.cfi_endproc            ##close of .cfi_startproc

$ gcc -g -nostdlib -o hi hi.s && ./hi Hello

知道了! 试图弄清楚什么是CFAhttp://dwarfstd.org/doc/DWARF4.pdf第 6.4 节

在堆栈上分配的内存区域称为“调用帧”。 调用帧由堆栈上的地址标识。我们参考 该地址作为规范帧地址或 CFA。通常, CFA 被定义为调用时堆栈指针的值 上一帧中的站点(可能与其上的值不同) 进入当前帧)

那么所有 .cfi_def_cfa_offset、.cfi_offset 和 .cfi_def_cfa_register 所做的就是计算, 并操作堆栈。但这个程序根本不需要堆栈,所以不妨也删除它

$ 猫嗨

.file   "hi.c"          ##tells 'as' that we are about to start a new logical file
    .section    .rodata     ##assembles the following code into section '.rodata'
.LC0:                   ##.LC0, .LFB0, .LFE0 are just local labels; symbols that
                    ##  are guaranteed to be unique over the source code
                    ##  that allow the compiler to use names/simple notation
                    ##  to reference sections of code
                    ##But here, only .LC0 is actually referenced in the code

.string "Hello"         
.text


.globl  _start
_start:
    .cfi_startproc          ##used at the beginning of each function that should have an
                    ##entry in .eh_frame. It initializes some internal data
                    ##structures. Don't forget to close by .cfi_endproc
    leaq    .LC0(%rip), %rsi
    movq    $1, %rax
    movq    $1, %rdi
    movq    $5, %rdx
    syscall

xor %rdi,%rdi   
mov $60, %rax
syscall
.cfi_endproc            ##close of .cfi_startproc

.cfi_startproc :

用在应该有一个条目的每个函数的开头 .eh_frame

什么是 eh_frame“当使用支持异常的语言(例如 C++)时,必须向运行时环境提供附加信息,以描述在异常处理过程中展开的调用帧。此信息包含在特殊部分 .eh_frame 和 .eh_framehdr 中”。

不需要异常处理,不使用C++

$ 猫嗨

.section    .rodata     
.LC0:                   

.string "Hello"         

.text
.globl  _start
_start:
    leaq    .LC0(%rip), %rsi
    movq    $1, %rax
    movq    $1, %rdi
    movq    $5, %rdx
    syscall

xor %rdi,%rdi   
mov $60, %rax
syscall
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从 64 位汇编调用 C 函数 的相关文章

  • 为什么这个未使用的变量没有被优化掉?

    我使用了 Godbolt 的 CompilerExplorer 我想看看某些优化有多好 我的最小工作示例是 include
  • 为什么 GCC 交叉编译不构建“crti.o”?

    在尝试为arm构建gcc 4 x x交叉编译器时 我陷入了缺失的困境crti o文件在 BUILD DIR gcc子目录 An strace在顶层Makefile表明编译后的xgcc正在调用交联器ld with crti o 作为一个论点
  • gcc 中的“假设”子句

    gcc 最新版本 4 8 4 9 是否有类似于以下的 假设 子句 assume 内置icc支持吗 例如 assume n 8 0 从 gcc 4 8 2 开始 gcc 中没有 assume 的等效项 我不知道为什么 这会非常有用 马夫索建议
  • AVX-512 指令编码 - {er} 含义

    在 Intel x86 指令集参考中 有许多 AVX 512 指令在指令中具有可选的 er 例如 VADDPD 的一种形式定义为 EVEX NDS 512 66 0F W1 58 r VADDPD zmm1 k1 z zmm2 zmm3 m
  • 如何在程序中将自己缝合到自己的尾部,无限循环地封装 64KB 代码段?

    如果指令的顺序执行经过偏移量 65535 则8086将从同一代码段中的偏移量 0 处获取下一个指令字节 接下来的 COM 程序利用这一事实 不断将其整个代码 总共 32 个字节 缝合到自己的尾部 环绕在 64KB 代码段中 你可以称之为二元
  • 如何编译GCC生成的asm?

    我正在玩一些汇编代码 有些事情困扰着我 我编译这个 include
  • Ubuntu 11.10 上的 c 数学链接器问题 [重复]

    这个问题在这里已经有答案了 我从 Ubuntu 升级后出现了一些奇怪的错误 10 11 11 04 i dont know 到 11 10 我正在得到一个undefined reference to sqrt 使用 math h 时并与 l
  • C99 中数组的静态大小[重复]

    这个问题在这里已经有答案了 一个非常简单的 C 程序 include
  • 在 x86 程序集中存储大量布尔值的最佳方法是什么?

    最近我一直在处理充满布尔值的大型数组 目前 我将它们存储在 bss部分有一个 space指令 它允许我创建字节数组 但是 由于我只需要存储布尔值 因此我希望从数组中逐位读取和写入数据 目前 我能想到的最好方法是有一个 space指令所需存储
  • 高效memcspn

    有谁知道 memcspn 函数的有效实现吗 它的行为应该类似于 strcspn 但在内存缓冲区中查找跨度 而不是在以 null 结尾的字符串中查找跨度 目标编译器是 VisualC 谢谢 卢卡 一种近乎最佳的实现 size t memcsp
  • NASM 中的 equ 和 db 有什么区别?

    len equ 2 len db 2 它们是否相同 产生可以用来代替的标签2 如果不是 那么每种申报表的优点或缺点是什么 它们可以互换使用吗 第一个是equate 与 C 类似 define len 2 因为它实际上并没有在最终代码中分配任
  • clang 实例化后静态成员初始化

    这样的代码可以用 GCC 编译 但 clang 3 5 失败 include
  • 32 位到 64 位内联汇编移植

    我有一段 C 代码 在 GNU Linux 环境下用 g 编译 它加载一个函数指针 它如何执行并不重要 使用一些内联汇编将一些参数推送到堆栈上 然后调用该函数 代码如下 unsigned long stack 1 23 33 43 save
  • Pyaudio 安装错误 - “命令‘gcc’失败,退出状态 1”

    我正在运行 Ubuntu 11 04 Python 2 7 1 并想安装 Pyaudio 于是我跑了 sudo easy install pyaudio 在终端中 进程退出并显示以下错误消息 Searching for pyaudio Re
  • 为什么X86中没有NAND、NOR和XNOR指令?

    它们是您可以在计算机上执行的最简单的 指令 之一 它们是我亲自实施的第一个指令 执行 NOT AND x y 会使执行时间和依赖链长度和代码大小加倍 BMI1 引入了 andnot 这是一个有意义的补充 是一个独特的操作 为什么不是这个问题
  • movzbl(%rdi, %rcx, 1), %ecx 在 x86-64 汇编中意味着什么?

    我想我明白 movzbl rdi rcx 1 ecx 意思是 将零扩展字节移至长整型 并表示将 ecx 扩展为 32 位 但我不完全确定语法 rdi rcx 1 指的是什么 我在某处看到该语法指的是 Base Index Scale 但我找
  • 为什么pow函数比简单运算慢?

    从我的一个朋友那里 我听说 pow 函数比简单地将底数乘以它的指数的等价函数要慢 例如 据他介绍 include
  • GCC 的“-Wl,option”和“-Xlinker option”语法之间有区别吗?

    我一直在查看一些配置文件 并且看到它们都被使用 尽管在不同的体系结构上 如果您在 Linux 机器上使用 GCC 将选项传递给链接器的两种语法之间有区别吗 据我所知 阅读 GCC 手册时 他们的解释几乎相同 From man gcc Xli
  • 查找哪些页面不再与写入时复制共享

    假设我在 Linux 中有一个进程 我从中fork 另一个相同的过程 后forking 因为原始进程将开始写入内存 Linux写时复制机制将为进程提供与分叉进程使用的不同的唯一物理内存页 在执行的某个时刻 我如何知道原始进程的哪些页面已被写
  • 使用 Gas 生成与位置无关的代码 (-fPIC)

    我尝试在 x86 64 上创建共享库但失败 问题归结为以下代码 请不要介意 它没有多大意义 section data newline ascii n section text globl write newline type write n

随机推荐

  • 在python中将字符串日期转换为日期格式?

    如何在Python中将下面的字符串日期转换为日期格式 input date 15 MARCH 2015 expected output 2015 03 15 我尝试使用datetime strftime and datetime strpt
  • Python 中的常量究竟由什么构成?

    PEP 8 规定 that 常量通常在模块级别定义并写在所有模块中 大写字母用下划线分隔单词 例子包括MAX OVERFLOW and TOTAL 我知道这只是一个命名约定 但我很想知道是否有官方或广泛接受的定义来定义常量与半私有变量的实际
  • 将子进程的标准输出重定向到 2 个或更多子进程的标准输入

    基本上我想学习如何使用stdout of one subprocess say proc1 as stdin2 个或更多其他subprocesses say proc2 proc3 在Python中 你好 我需要zcat gz 文件并使用发
  • 带枚举的单例与带双重检查锁定的单例

    我想知道在多线程环境中实践中哪一个更好 我将 Singleton 理解为 Enum 类型在加载类时创建一个实例 除此之外我没有看到任何其他重要的东西 有什么优点和缺点吗 单例作为Enum type public enum Singleton
  • 如何分离由 subprocess.call 运行的程序?

    我正在使用 subprocess call 使用默认应用程序打开 pdf 文件 如下所示 subprocess call xdg open pdf stderr STDOUT 但是 当运行该进程时 该进程已附加到终端 我想将其分离 基本上
  • Windows 中是否有相当于 cron 的工具?

    我想知道是否有同等的cron在 Windows 中以及如何使用 PHP 以编程方式使用它 Windows 有Scheduled Tasks控制面板小程序 或更高版本的 Windows 上的管理控制台插件 但您也可以通过以下方式访问它scht
  • 防止应用程序升级时数据丢失

    我们开发了一款 Android 应用程序 并通过 MDM 移动设备管理 Soti Mobi 控制 推送新的安装和更新 发送的更新会静默升级应用程序 无需用户确认 因此 如果用户正在使用应用程序 应用程序会自动停止 升级并从头开始启动 因此用
  • grep (bash) 多行模式

    在 bash 4 3 46 1 中 我有一些多行所谓的 fasta 记录 其中每个记录都是由 gt name 和以下几行 DNA 序列 AGCTNacgtn 启动 这里有三个记录 gt chr1 AGCTACTTTT AGGGNGGTNN
  • 请求了 n 条推文,但 API 只能返回 0 错误

    我无法使用 R 中的 twitteR 包检索任何推文 例如此请求 nbahash tweets searchTwitter nba since 2013 01 01 until 2014 02 25 n 20 显示此警告消息 警告消息 在
  • 如何在 Dropbox API v2 中创建不过期令牌?

    正如标题所说 最近 Dropbox API 改版后 Token 开始有过期时间了 以前如果我不撤销的话 Token是可以永久使用的 也许出于安全原因添加了过期时间 然而 这对于我的应用来说非常不方便 我需要上传文件很长时间 可能一次超过一个
  • 使用另一个数组从数组中删除条目

    不知道该怎么做 所以非常感谢任何帮助 说我有 const array1 1 1 2 3 4 const array2 1 2 所需输出 const result 1 3 4 我想比较一下array1 and array2对于每个条目arra
  • 从 Zend Controller 插件中获取视图对象

    在我的控制器中 我有一个 postDispatch 来整合我的 FlashMessenger 消息 public function postDispatch messages this gt helper gt getHelper Flas
  • 在更新之前的代码签名证书后,如何在 Windows 10 中保留 SmartScreen 筛选器的声誉?

    我在过去 3 年里拥有代码签名证书 当我用它签署我的软件时 签名没有导致任何问题SmartScreen从 Internet 下载软件时出现警告 该证书本月到期 因此我在同一家公司续签了三年 它造成的问题是 现在当我签署我的软件时 新证书在
  • 将任务放入序列容器后无法连接到数据库

    我有一个工作完美的包 直到我决定将它的一些任务放入序列容器中 更多关于我为什么要这样做的信息 在我的情况下如何进行 SSIS 交易 现在 我不断收到错误 Execute SQL Task Error Failed to acquire co
  • 处理每个数组条目的替换,无需 Eval

    例如 我有一个任意字符串数组a 1st 2nd string 3rd nstring n 例如 我想将这些字符串传递给将其参数解释为文件的命令paste 对于固定数量的变量 我们可以使用过程替换 paste lt printf s var1
  • GCP Secret Manager:Spring Boot 应用程序中未解析环境变量和密钥

    下列的本指南 and 这段代码示例 我的里面有这个application deploy yaml spring following cloud gcp project id PROJECT ID Set during build proce
  • 按天分组并仍然显示没有行的日子?

    我有一个日志表 其中包含一个名为 logTime 的日期字段 我需要显示日期范围内的行数以及每天的记录数 问题是我仍然想展示那些日子没有记录 是否可以仅使用 SQL 来完成此操作 Example SELECT logTime COUNT F
  • 如何处理glBufferData期间的GL_OUT_OF_MEMORY错误?

    OpenGL 参考文献提到了GL OUT OF MEMORY error 记录此错误后 除了错误标志的状态外 GL 的状态未定义 功能glBufferData如果无法消化给定的数据 可能会生成此错误 但另一方面 API 似乎没有提供任何方法
  • Swift 5 与 SwiftUI:如何动态更改环境区域设置

    我必须让用户从列表中选择应用程序的语言 将下一行放入 SceneDelegate 中效果很好 因为它在开头加载指定的语言 window rootViewController UIHostingController rootView Cont
  • 从 64 位汇编调用 C 函数

    在 ubuntu 16 04 上 cat hola asm extern puts global main section text main mov rdi message call puts ret message db Hola 0