在尝试让一些旧代码重新工作时(https://github.com/chaos4ever/chaos/blob/master/libraries/system/system_calls.h#l387,FWIW),我发现gcc
的一些语义似乎在最近的10-15年中发生了非常微妙但仍然危险的变化...:p
该代码在gcc
的旧版本(如2.95)中可以很好地工作。总之,这里是代码:
static inline return_type system_call_service_get(const char *protocol_name, service_parameter_type *service_parameter,
tag_type *identification)
{
return_type return_value;
asm volatile("pushl %2\n"
"pushl %3\n"
"pushl %4\n"
"lcall %5, $0"
: "=a" (return_value),
"=g" (*service_parameter)
: "g" (identification),
"g" (service_parameter),
"g" (protocol_name),
"n" (SYSTEM_CALL_SERVICE_GET << 3));
return return_value;
}
上面代码的问题是gcc
(在我的例子中为4.7)将其编译为以下asm代码(AT&T语法):
# 392 "../system/system_calls.h" 1
pushl 68(%esp) # This pointer (%esp + 0x68) is valid when the inline asm is entered.
pushl %eax
pushl 48(%esp) # ...but this one is not (%esp + 0x48), since two dwords have now been pushed onto the stack, so %esp is not what the compiler expects it to be
lcall $456, $0
# Restoration of %esp at this point is done in the called method (i.e. lret $12)
问题是:变量(identification
和protocol_name
)在调用上下文的堆栈上。因此gcc
(进行了优化,不确定是否重要)将从那里获取值并将其交给内联asm部分。但由于我正在堆栈上推入数据,因此gcc
计算的偏移量将在第三次调用(pushl48(%ESP)
)时减少8。:)
这花了我很长时间才弄明白,一开始对我来说并不是很明显。
当然,最简单的方法是使用r
输入约束,以确保该值位于寄存器中。但还有其他更好的办法吗?当然,一个明显的方法是重写整个系统调用接口,以不在堆栈上推入任何东西(并使用寄存器代替,例如Linux),但这不是我今晚想要做的重构...
当天晚上更新:我确实找到了一个相关的gcc
ML线程(https://gcc.gnu.org/ML/gcc-help/2011-06/msg00206.html),但似乎没有什么帮助。似乎在clobber列表中指定%ESP
应该会使它与%EBP
产生偏移量,但这并不起作用,我怀疑-o2-fomit-frame-pointer
在这里有作用。我已经启用了这两个标志。
什么有效什么无效:
>
我尝试省略-fomit-frame-pointer
。没有任何影响。我将%ESP
、ESP
和SP
包含在clobbers列表中。
我尝试省略-fomit-frame-pointer
和-o3
。这实际上生成了可以工作的代码,因为它依赖于%ebp
而不是%esp
。
pushl 16(%ebp)
pushl 12(%ebp)
pushl 8(%ebp)
lcall $456, $0
我尝试跳过-fomit-frame-pointer
而只使用-o2
。破译的代码,没有堆栈帧。
我尝试使用-o1
。破译的代码,没有堆栈帧。
我尝试将cc
添加为clobber。不行,没有任何区别。
static inline return_type system_call_service_get(const char *protocol_name, service_parameter_type *service_parameter,
tag_type *identification)
{
return_type return_value;
asm volatile("pushl %2\n"
"pushl %3\n"
"pushl %4\n"
"lcall %5, $0"
: "=a" (return_value),
"=g" (*service_parameter)
: "ri" (identification),
"ri" (service_parameter),
"ri" (protocol_name),
"n" (SYSTEM_CALL_SERVICE_GET << 3));
return return_value;
}
#APP
# 392 "../system/system_calls.h" 1
pushl %esi
pushl %eax
pushl %ebx
lcall $456, $0
我正在尝试同时处理MSVC和GCC编译器,同时更新这个代码库以在GCC上工作。但我不确定GCCs内联ASM是如何工作的。现在我并不擅长将ASM转换为C,否则我就会使用C而不是ASM。 我假设ROR13的工作方式类似于,但代码不会产生相同的输出。 什么是将这个内联ASM翻译成GCC的正确方法,或者这个代码的C翻译是什么?
GCC扩展内联汇编 使用GCC扩展内联汇编的例子如下: #define read_cr0() ({ \ unsigned int __dummy; \ __asm__( \ "movl %%cr0,%0\n\t" \ :"=r" (__dummy)); \ __dummy; \ }) 它代表什么含义呢?这需要从其基本格式讲起。GCC扩展内联汇编的基本格式是: asm [volat
GCC基本内联汇编 GCC 提供了两内内联汇编语句(inline asm statements):基本内联汇编语句(basic inline asm statement)和扩展内联汇编语句(extended inline asm statement)。GCC基本内联汇编很简单,一般是按照下面的格式: asm("statements"); 例如: asm("nop"); asm("
谢谢你的帮助。
问题内容: 溢出, 如何仅使用内联汇编实现putchar(char)过程?我想在x86-64汇编中做到这一点。我这样做的原因是实现我自己的标准库(或至少一部分)。这是我到目前为止的内容: 我正在编译: 感谢您的帮助! 问题答案: 使用GNU C内联asm时,请 使用约束来告诉编译器您想要的东西 ,而不是使用asm模板中的指令“手动”进行。 对于和,我们只需要为模板,用约束来设置所有寄存器输入(与所
问题内容: 是否可以从内联汇编块中使用syscall编写单个字符?如果是这样,怎么办?它应该看起来像“东西”: $ 80是ascii中的“ P”,但是什么也不会返回。 任何建议,不胜感激! 问题答案: 就像是 添加 :请注意,我曾经将char的有效地址加载到寄存器中;对于我尝试$ 0和$ 1的价值,它似乎仍然可以工作… 避免使用外部字符 注意:它之所以有效是因为Intel处理器的字节序!:D