热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Can'tcallCstandardlibraryfunctionon64-bitLinuxfromassembly(yasm)code

如何解决《Can'tcallCstandardlibraryfunctionon64-bitLinuxfromassembly(yasm)code》经验,为你挑选了2个好方法。

I have a function foo written in assembly and compiled with yasm and GCC on Linux (Ubuntu) 64-bit. It simply prints a message to stdout using puts(), here is how it looks:

bits 64

extern puts
global foo

section .data

message:
  db 'foo() called', 0

section .text

foo:
  push rbp
  mov rbp, rsp
  lea rdi, [rel message]
  call puts
  pop rbp
  ret

It is called by a C program compiled with GCC:

extern void foo();

int main() {
    foo();
    return 0;
}

Build commands:

yasm -f elf64 foo_64_unix.asm
gcc -c foo_main.c -o foo_main.o
gcc foo_64_unix.o foo_main.o -o foo
./foo

Here is the problem:

When running the program it prints an error message and immediately segfaults during the call to puts:

./foo: Symbol `puts' causes overflow in R_X86_64_PC32 relocation
Segmentation fault

After disassembling with objdump I see that the call is made with the wrong address:

0000000000000660 :
 660:   90                      nop
 661:   55                      push   %rbp
 662:   48 89 e5                mov    %rsp,%rbp
 665:   48 8d 3d a4 09 20 00    lea    0x2009a4(%rip),%rdi
 66c:   e8 00 00 00 00          callq  671       <-- here
 671:   5d                      pop    %rbp
 672:   c3                      retq

(671 is the address of the next instruction, not address of puts)

However, if I rewrite the same code in C the call is done differently:

645:   e8 c6 fe ff ff          callq  510 

i.e. it references puts from the PLT.

Is it possible to tell yasm to generate similar code?



1> Peter Cordes..:

您的gcc默认情况下正在构建PIE可执行文件(x86-64 Linux中不再允许使用32位绝对地址?)。

我不确定为什么,但是这样做时链接程序不会自动解析call putscall puts@plt。仍然会puts生成一个PLT条目,但call不会去那里。

在运行时,动态链接器尝试puts直接解析为该名称的libc符号并修复call rel32。但是符号距离+ -2 ^ 31远,因此我们收到有关R_X86_64_PC32重定位溢出的警告。目标地址的低32位是正确的,但高位不是。(因此,您call跳到了错误的地址)。


如果使用编译,您的代码对我有用gcc -no-pie -fno-pie call-lib.c libcall.o。该-no-pie是关键的部分:它的连接选项。您的YASM命令无需更改。

在制作传统的与位置相关的可执行文件时,链接程序会为您puts将调用目标的符号转换puts@plt为您,因为我们链接的是动态可执行文件(而不是将libc与静态链接gcc -static -fno-pie,在这种情况下,call可以直接转到libc函数。 )

无论如何,这就是为什么gcc进行编译时会发出call puts@plt(GAS语法)-fpie(您的桌面上的默认设置,而不是https://godbolt.org/上的默认设置),而只是call puts在使用时进行编译的原因-fno-pie


请参阅@plt在这里是什么意思?有关PLT的更多信息,以及几年前Linux上动态库的抱歉状态。(现代gcc -fno-plt就像该博客文章中的想法之一。)


顺便说一句,更准确/更具体的原型将使gcc避免在调用前将EAX归零foo

extern void foo();在C中意味着extern void foo(...);
您可以将其声明为extern void foo(void);,这()在C ++中意味着。C ++不允许保留未指定args的函数声明。


asm改进

您也可以message输入section .rodata(只读数据,链接为文本段的一部分)。

您不需要堆栈框架,只需执行一些操作即可在调用之前将堆栈按16对齐。一个假人push rax会做。

或者我们可以puts通过跳转到它而不是调用它来进行尾调用,并且具有与该函数入口相同的堆栈位置。无论有无PIE,此功能均可使用。只需更换calljmp,只要RSP是在你自己的返回地址指向。

如果要使PIE可执行文件,则有两个选择

call puts wrt ..plt -通过PLT显式调用。

call [rel puts wrt ..got]-像gcc的-fno-plt代码生成样式一样,通过GOT条目进行间接调用。(使用相对于RIP的寻址模式来到达GOT,因此使用rel关键字)。

WRT =关于。NASM手册文档wrt ..plt,另请参见第7.9.3节:特殊符号和WRT。

通常,您将使用default rel文件的顶部,以便可以实际使用call [puts wrt ..got]并且仍然获得相对于RIP的寻址模式。您不能在PIE或PIC代码中使用32位绝对寻址模式。

call [puts wrt ..got]使用动态链接存储在GOT中的函数指针将其汇编为内存间接调用。(早期绑定,而不是惰性动态链接。)

NASM文档..got在9.2.3节中获取变量的地址。其他库中的函数是相同的:您从GOT获取了一个指针,而不是直接调用它,因为偏移量不是链接时常数,并且可能不适合32位。

YASM也接受call [puts wrt ..GOTPCREL](如AT&T语法)call *puts@GOTPCREL(%rip),但NASM不接受。

; don't use BITS 64.  You *want* an error if you try to assemble this into a 32-bit .o

default rel          ; RIP-relative addressing instead of 32-bit absolute by default; makes the [rel ...] optional

section .rodata            ; .rodata is best for constants, not .data
message:
  db 'foo() called', 0

section .text

global foo
foo:
    sub    rsp, 8                ; align the stack by 16

    ; PIE with PLT
    lea    rdi, [rel message]      ; needed for PIE
    call   puts WRT ..plt          ; tailcall puts
;or
    ; PIE with -fno-plt style code, skips the PLT indirection
    lea   rdi, [rel message]
    call  [rel  puts wrt ..got]
;or
    ; non-PIE
    mov    edi, message           ; more efficient, but only works in non-PIE / non-PIC
    call   puts                   ; linker will rewrite it into call puts@plt

    add   rsp,8                   ; remove the padding
    ret

在位置相关的可执行文件中,可以使用mov edi, messageRIP相对的LEA代替。它的代码较小,可以在大多数CPU的更多执行端口上运行。

在非PIE可执行文件中,您也可以使用call putsjmp puts让链接器对其进行排序,除非您想要更有效的no-plt样式动态链接。但是,如果您选择静态链接libc,我认为这是将jmp直接连接到libc函数的唯一方法。

(我认为静态链接非PIE的可能性就是为什么 ld愿意为非PIE(而不是PIE或共享库)自动生成PLT存根的原因。它要求您说出链接ELF共享对象时的含义。)

如果您确实使用call puts了PIE(call rel32),则只有将与位置无关的实现静态链接puts到您的PIE中才能起作用,因此整个事情就是一个可执行文件,它将在运行时加载到随机地址(通常是动态-linker机制),但根本不依赖libc.so.6



2> lockcmpxchg8..:

0xe8操作码后面跟着一个符号偏移量被应用到PC(已经由时间推进到下一指令)来计算分支目标。因此objdump,将分支目标解释为0x671

YASM正在渲染零,因为它可能已在该偏移量上放置了重定位,这就是它要求加载程序puts在加载期间填充正确偏移量的方式。加载程序在计算重定位时遇到溢出,这可能表明它puts与您的调用之间的偏移量比32位带符号偏移量所表示的偏移量还大。因此,加载程序无法修复此指令,您将当机。

66c: e8 00 00 00 00显示未填充的地址。如果您在重定位表中查找,您应该在上看到重定位0x66d。汇编器使用全零的重定位填充地址/偏移量并不少见。

此页面提示YASM有一个WRT指令,可以控制使用.got.plt等等。

根据NASM文档上的 S9.2.5 ,看起来您可以使用CALL puts WRT ..plt(假定YASM具有相同的语法)。


推荐阅读
  • 2018 HDU 多校联合第五场 G题:Glad You Game(线段树优化解法)
    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6356在《Glad You Game》中,Steve 面临一个复杂的区间操作问题。该题可以通过线段树进行高效优化。具体来说,线段树能够快速处理区间更新和查询操作,从而大大提高了算法的效率。本文详细介绍了线段树的构建和维护方法,并给出了具体的代码实现,帮助读者更好地理解和应用这一数据结构。 ... [详细]
  • 在Android平台中,播放音频的采样率通常固定为44.1kHz,而录音的采样率则固定为8kHz。为了确保音频设备的正常工作,底层驱动必须预先设定这些固定的采样率。当上层应用提供的采样率与这些预设值不匹配时,需要通过重采样(resample)技术来调整采样率,以保证音频数据的正确处理和传输。本文将详细探讨FFMpeg在音频处理中的基础理论及重采样技术的应用。 ... [详细]
  • 在洛谷 P1344 的坏牛奶追踪问题中,第一问要求计算最小割,而第二问则需要找到割边数量最少的最小割。通过为每条边附加一个单位权值,可以在求解最小割时优先选择边数较少的方案,从而同时解决两个问题。这种策略不仅简化了问题的求解过程,还确保了结果的最优性。 ... [详细]
  • 手指触控|Android电容屏幕驱动调试指南
    手指触控|Android电容屏幕驱动调试指南 ... [详细]
  • 在C++程序中,文档A的每一行包含一个结构体数据,其中某些字段可能包含不同数量的数字。需要将这些结构体数据逐行读取并存储到向量中,随后不仅在控制台上显示,还要输出到新创建的文档B中。希望得到指导,感谢! ... [详细]
  • 使用 ListView 浏览安卓系统中的回收站文件 ... [详细]
  • 在Linux系统中,网络配置是至关重要的任务之一。本文详细解析了Firewalld和Netfilter机制,并探讨了iptables的应用。通过使用`ip addr show`命令来查看网卡IP地址(需要安装`iproute`包),当网卡未分配IP地址或处于关闭状态时,可以通过`ip link set`命令进行配置和激活。此外,文章还介绍了如何利用Firewalld和iptables实现网络流量控制和安全策略管理,为系统管理员提供了实用的操作指南。 ... [详细]
  • 在2019年寒假强化训练中,我们深入探讨了二分算法的理论与实践应用。问题A聚焦于使用递归方法实现二分查找。具体而言,给定一个已按升序排列且无重复元素的数组,用户需从键盘输入一个数值X,通过二分查找法判断该数值是否存在于数组中。输入的第一行为一个正整数,表示数组的长度。这一训练不仅强化了对递归算法的理解,还提升了实际编程能力。 ... [详细]
  • Codeforces 605C:Freelancer's Dreams —— 凸包算法解析与题解分析 ... [详细]
  • Vue ElementUI 实现邮箱地址自动补全功能详解 ... [详细]
  • 微信小程序实现类似微博的无限回复功能,内置云开发数据库支持
    本文详细介绍了如何利用微信小程序实现类似于微博的无限回复功能,并充分利用了微信云开发的数据库支持。文中不仅提供了关键代码片段,还包含了完整的页面代码,方便开发者按需使用。此外,HTML页面中包含了一些示例图片,开发者可以根据个人喜好进行替换。文章还将展示详细的数据库结构设计,帮助读者更好地理解和实现这一功能。 ... [详细]
  • 本文介绍了如何在iOS平台上使用GLSL着色器将YV12格式的视频帧数据转换为RGB格式,并展示了转换后的图像效果。通过详细的技术实现步骤和代码示例,读者可以轻松掌握这一过程,适用于需要进行视频处理的应用开发。 ... [详细]
  • 题目链接:https://www.luogu.com.cn/problem/P6453在解决 COCI 2008-2009 第四轮 PERIODNI 问题时,我们需要逐行分析。由于一行中的字符若被断开则不再视为同一行,因此每行的最大矩形区域需要单独计算。通过这种方法,可以确保每层都能找到其最大连续子矩形,从而有效解决问题。 ... [详细]
  • 题目链接: Caninepoetry问题概述:给定一个仅包含小写字母的字符串,允许将任意位置的字符修改为任意其他小写字母。目标是通过最少次数的修改,使字符串中所有长度大于1的子串均满足特定条件。本文详细分析了该问题,并运用思维与贪心算法,提出了一种高效解决方案。通过对字符串的深入解析,我们探讨了如何在最小化修改次数的同时,确保所有子串符合要求。 ... [详细]
  • 链栈虽然通常以数组作为底层实现,但也可以采用链表来构建Stack类。在这种情况下,空堆栈通过NULL指针表示。当新元素被压入堆栈时,它会被添加到链表的头部,从而实现高效的入栈操作。此外,出栈操作则通过移除链表头部的节点来完成,确保了操作的时间复杂度为O(1)。这种设计不仅简化了内存管理,还提高了动态数据处理的灵活性。 ... [详细]
author-avatar
_____Fmr丶
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有