基于此tutorial,我试图在64位Linux上将SELECT [p].[AgeInMonths], CASE
WHEN [p].[TitleTypeId] IS NULL
THEN NULL ELSE [p.TitleType].[Title]
END, [p].[FirstName], [p].[NickName], [p].[MiddleName], [p].[LastName], CASE
WHEN [p].[SuffixTypeId] IS NULL
THEN NULL ELSE [p.SuffixType].[Suffix]
END, [p].[DateOfBirth], [p].[DateOfDeath]
FROM [dbo].[People] AS [p]
INNER JOIN [dbo].[SuffixTypes] AS [p.SuffixType] ON [p].[SuffixTypeId] = [p.SuffixType].[Id]
INNER JOIN [dbo].[TitleTypes] AS [p.TitleType] ON [p].[TitleTypeId] = [p.TitleType].[Id]`
写入控制台。编译没有引起任何错误,但我在控制台上也没有文本。我不知道出了什么问题。
write.s :
Hello World
main.c中:
.data
SYSREAD = 0
SYSWRITE = 1
SYSEXIT = 60
STDOUT = 1
STDIN = 0
EXIT_SUCCESS = 0
message: .ascii "Hello, world!\n"
message_len = .-message
.text
.globl _write
_write:
pushq %rbp
movq %rsp, %rbp
movq $SYSWRITE, %rax
movq $STDOUT, %rdi
movq $message, %rsi
movq $message_len, %rdx
syscall
popq %rbp
ret
编译:
extern void write(void);
int main (int argc, char **argv)
{
write();
return 0;
}
答案 0 :(得分:2)
好的,所以我的代码有两个错误:
1)我将我命名为函数'write',它是常见的c名称,我需要重命名它。
2)在函数名称中,我不应该使用下划线。
正确的代码:
writehello.s
.data
SYSREAD = 0
SYSWRITE = 1
SYSEXIT = 60
STDOUT = 1
STDIN = 0
EXIT_SUCCESS = 0
message: .ascii "Hello, world!\n"
message_len = .-message
.text
#.global main
#main:
#call write
#movq $SYSEXIT, %rax
#movq $EXIT_SUCCESS, %rdi
#syscall
#********
.global writehello
writehello:
pushq %rbp
movq %rsp, %rbp
movq $SYSWRITE, %rax
movq $STDOUT, %rdi
movq $message, %rsi
movq $message_len, %rdx
syscall
popq %rbp
ret
的main.c
extern void writehello(void);
int main (int argc, char **argv)
{
writehello();
return 0;
}
编译保持原样:)感谢所有帮助过的人!
答案 1 :(得分:2)
您正在阅读的教程不太对劲。 ELF(可执行和可链接格式)可执行文件中的全局符号有两种不同的约定。一种惯例是所有全局C符号都应以_
为前缀,另一种约定不以C符号为前缀。 在GNU / Linux中,尤其是在x86-64 ABI中,全局符号不以_
作为前缀。但是,您链接的教程可能适用于没有使用GNU libc的Linux / ELF的其他编译器。
现在,原始代码中发生的事情是,汇编程序函数在C代码中显示为_write
,而不是write
。相反,write
符号位于libc
(write(2)
系统调用的包装器)中:
ssize_t write(int fd, const void *buf, size_t count);
现在您将此 write
声明为函数void write(void);
,当您调用它时,会导致未定义的行为。您可以使用strace ./program
找出它所调用的系统:
% strace ./program
...
write(1, "\246^P\313\374\177\0\0\0\0\0\0\0\0"..., 140723719521144) = -1 EFAULT (Bad address)
...
所以它调用write
系统调用不是你想要的参数,而是调用提供给glibc write
包装器的寄存器中的垃圾。 (实际上"垃圾"在这里已知 - 第一个参数是argc
,第二个参数是argv
的值,第三个参数是{{1}的值})。并且由于内核注意到从char **environ
和140723719521144字节开始的缓冲区并未完全包含在映射的地址空间中,因此它从该系统调用返回(void*)argv
。结果:没有崩溃,没有消息。
EFAULT
在C中不是保留字。它是一个函数,可能是POSIX中的宏。您可能覆盖它,链接顺序很重要 - 如果您的程序定义write
,其他代码将链接到此定义而不是glibc中找到的代码。但是,这意味着调用write
的其他代码最终会调用不兼容的函数。
因此,解决方案是不使用GNU libc中的函数名称或您链接的任何其他库中的函数。因此,在汇编程序中,您可以使用:
write
然后
.global writehello
writehello:
你自己已经发现了。