更正 sys_uname 的内联汇编代码
Correct inline assembly code for sys_uname
我已经为系统调用sys_uname编写了内联汇编代码,但似乎不正确。
#include <sys/utsname.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/syscalls.h>
#include <string.h>
struct utsname stroj;
__asm__ volatile("pushl [=11=];"
"pushl %%ebx;"
"pushl [=11=];"
"int [=11=]x80;"
:
: "a" (SYS_uname), "b" (&stroj)
);
//uname(&stroj); -> when I do this it works, but again, I want to use inline assembly
write(1, stroj.nodename, strlen(stroj.nodename)); // 1 = stdout
是否有一些明显的问题我没有解决?此写入没有打印出任何内容,字面意思是“”。
此答案假定您希望直接使用系统调用而不是通过 C 库函数是有原因的。
正确的内联程序集版本可能如下所示:
#include <sys/utsname.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/syscall.h>
#include <string.h>
#include <unistd.h>
/* SYS_uname has the value 164 */
/* #define SYS_uname 164 */
#define SYS_uname SYS_freebsd4_uname
int main()
{
u_int syscallnum = SYS_uname;
struct utsname stroj;
asm("push %[stroj]\n\t"
"push %%eax\n\t" /* Required dummy value for int 0x80 */
"int [=10=]x80\n\t"
"add , %%esp" /* 2*4 bytes removed from stack */
: "+a"(syscallnum) /* error code also returned in syscallnum */
: [stroj]"r"(&stroj)
: "memory");
write(1, stroj.nodename, strlen(stroj.nodename));
return 0;
}
对于FreeBSD 32-bit system calls,参数以相反的顺序压入堆栈。在发出 int [=15=]x80
之前,必须将虚拟值(任何值)压入堆栈。系统调用后需要调整栈指针ESP。任何可能改变的寄存器也需要处理。 int [=15=]x80
将 return 在 EAX 中的错误代码。 return 上面的代码将该值返回到 syscallnum
变量中。如果在内联汇编中修改寄存器并且不让 GCC 知道它可能会导致通常难以追查的未定义行为。
如果您通过寄存器传递地址,您将需要添加内存操作数(即使它们是虚拟的)以指定正在读取 and/or 写入寄存器中指针处的数据。或者,您可以指定 memory
clobber,尽管这是一种更蛮力的方法,但它可能更容易理解。
GCC 的内联汇编功能强大,但很难正确使用,如果错误,可能会导致意外行为。您应该只将内联汇编用作可用于调用大多数系统调用的 last resort. FreeBSD has a syscall
function。
您可以将上面的内联程序集编写为:
asm(
"push %[stroj]\n\t"
"push %%eax\n\t" /* Required dummy value for int 0x80 */
"int [=11=]x80\n\t"
"add , %%esp" /* 2*4 bytes removed from stack */
: "+a"(syscallnum), /* error code also returned in syscallnum */
"=m"(stroj)
: [stroj]"r"(&stroj));
FreeBSD 2+ 不支持过时的SYS_uname
如果您尝试 运行 上面的代码,您会发现它没有 return 任何东西。如果你使用程序 TRUSS 和像 truss ./progname
这样的命令,你应该在输出中看到类似这样的东西:
obs_uname(0xffffc6f8,0x0,0x0,0x0,0x0,0x0) ERR#78 'Function not implemented'
这是因为 FreeBSD 2+ doesn't support the SYS_uname
system call and is now considered obsolete. FreeBSD's libc uname
makes calls to SYS___sysctl
填充了 utsname
结构的字段。从命令行你可以查询 nodename
使用:
sysctl kern.hostname
你可以这样通过系统调用调用sysctl
:
#include <unistd.h>
#include <sys/syscall.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/sysctl.h>
#define OSNAME_MAX_LEN 256
/* SYS___sysctl has the value 202 */
/* #define SYS___sysctl 202 */
int main(void)
{
char osname[OSNAME_MAX_LEN];
size_t osnamelen = sizeof(osname) - 1;
int name[] = {CTL_KERN, KERN_HOSTNAME};
u_int namelen = sizeof(name) / sizeof(name[0]);
char * old = osname;
size_t * oldlenp = &osnamelen;
u_int syscallnum = SYS___sysctl;
asm("push %[newlen]\n\t"
"push %[new]\n\t"
"push %[oldlenp]\n\t"
"push %[old]\n\t"
"push %[namelen]\n\t"
"push %[name]\n\t"
"push %%eax\n\t" /* Required dummy value */
"int [=13=]x80\n\t"
"add , %%esp" /* 7*4=28 bytes to remove from stack */
: "+a"(syscallnum) /* error code also returned in syscallnum */
: [name]"r"(name), [namelen]"r"(namelen),
[old]"r"(old) , [oldlenp]"r"(oldlenp),
[new]"i"(NULL), [newlen]"i"(0)
: "memory");
if (syscallnum) {
return EXIT_FAILURE;
}
osname[osnamelen]='[=13=]'; /* Ensure the OS Name is Null terminated */
printf("This machine's node name is %s\n", osname);
return EXIT_SUCCESS;
}
当内联汇编调整 ESP(push
等)时,它可能导致 GCC 生成并通过约束传递的内存操作数指向错误的内存位置。如果任何数据都放在堆栈上,则尤其如此。为避免此问题,最简单的方法是通过寄存器传递地址。
使用 syscall
函数而不是内联汇编也可以这样写:
#include <unistd.h>
#include <sys/syscall.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/sysctl.h>
#define OSNAME_MAX_LEN 256
/* SYS___sysctl has the value 202 */
/* #define SYS___sysctl 202 */
int main(void)
{
char osname[OSNAME_MAX_LEN];
size_t osnamelen = sizeof(osname) - 1;
int name[] = {CTL_KERN, KERN_HOSTNAME};
u_int namelen = sizeof(name) / sizeof(name[0]);
char * old = osname;
size_t * oldlenp = &osnamelen;
if (syscall(SYS___sysctl, name, namelen, old, oldlenp, NULL, 0) == -1) {
perror("sysctl");
return EXIT_FAILURE;
}
osname[osnamelen]='[=14=]'; /* Ensure the OS Name is Null terminated */
printf("This machine's node name is %s\n", osname);
return EXIT_SUCCESS;
}
我已经为系统调用sys_uname编写了内联汇编代码,但似乎不正确。
#include <sys/utsname.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/syscalls.h>
#include <string.h>
struct utsname stroj;
__asm__ volatile("pushl [=11=];"
"pushl %%ebx;"
"pushl [=11=];"
"int [=11=]x80;"
:
: "a" (SYS_uname), "b" (&stroj)
);
//uname(&stroj); -> when I do this it works, but again, I want to use inline assembly
write(1, stroj.nodename, strlen(stroj.nodename)); // 1 = stdout
是否有一些明显的问题我没有解决?此写入没有打印出任何内容,字面意思是“”。
此答案假定您希望直接使用系统调用而不是通过 C 库函数是有原因的。
正确的内联程序集版本可能如下所示:
#include <sys/utsname.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/syscall.h>
#include <string.h>
#include <unistd.h>
/* SYS_uname has the value 164 */
/* #define SYS_uname 164 */
#define SYS_uname SYS_freebsd4_uname
int main()
{
u_int syscallnum = SYS_uname;
struct utsname stroj;
asm("push %[stroj]\n\t"
"push %%eax\n\t" /* Required dummy value for int 0x80 */
"int [=10=]x80\n\t"
"add , %%esp" /* 2*4 bytes removed from stack */
: "+a"(syscallnum) /* error code also returned in syscallnum */
: [stroj]"r"(&stroj)
: "memory");
write(1, stroj.nodename, strlen(stroj.nodename));
return 0;
}
对于FreeBSD 32-bit system calls,参数以相反的顺序压入堆栈。在发出 int [=15=]x80
之前,必须将虚拟值(任何值)压入堆栈。系统调用后需要调整栈指针ESP。任何可能改变的寄存器也需要处理。 int [=15=]x80
将 return 在 EAX 中的错误代码。 return 上面的代码将该值返回到 syscallnum
变量中。如果在内联汇编中修改寄存器并且不让 GCC 知道它可能会导致通常难以追查的未定义行为。
如果您通过寄存器传递地址,您将需要添加内存操作数(即使它们是虚拟的)以指定正在读取 and/or 写入寄存器中指针处的数据。或者,您可以指定 memory
clobber,尽管这是一种更蛮力的方法,但它可能更容易理解。
GCC 的内联汇编功能强大,但很难正确使用,如果错误,可能会导致意外行为。您应该只将内联汇编用作可用于调用大多数系统调用的 last resort. FreeBSD has a syscall
function。
您可以将上面的内联程序集编写为:
asm(
"push %[stroj]\n\t"
"push %%eax\n\t" /* Required dummy value for int 0x80 */
"int [=11=]x80\n\t"
"add , %%esp" /* 2*4 bytes removed from stack */
: "+a"(syscallnum), /* error code also returned in syscallnum */
"=m"(stroj)
: [stroj]"r"(&stroj));
FreeBSD 2+ 不支持过时的SYS_uname
如果您尝试 运行 上面的代码,您会发现它没有 return 任何东西。如果你使用程序 TRUSS 和像 truss ./progname
这样的命令,你应该在输出中看到类似这样的东西:
obs_uname(0xffffc6f8,0x0,0x0,0x0,0x0,0x0) ERR#78 'Function not implemented'
这是因为 FreeBSD 2+ doesn't support the SYS_uname
system call and is now considered obsolete. FreeBSD's libc uname
makes calls to SYS___sysctl
填充了 utsname
结构的字段。从命令行你可以查询 nodename
使用:
sysctl kern.hostname
你可以这样通过系统调用调用sysctl
:
#include <unistd.h>
#include <sys/syscall.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/sysctl.h>
#define OSNAME_MAX_LEN 256
/* SYS___sysctl has the value 202 */
/* #define SYS___sysctl 202 */
int main(void)
{
char osname[OSNAME_MAX_LEN];
size_t osnamelen = sizeof(osname) - 1;
int name[] = {CTL_KERN, KERN_HOSTNAME};
u_int namelen = sizeof(name) / sizeof(name[0]);
char * old = osname;
size_t * oldlenp = &osnamelen;
u_int syscallnum = SYS___sysctl;
asm("push %[newlen]\n\t"
"push %[new]\n\t"
"push %[oldlenp]\n\t"
"push %[old]\n\t"
"push %[namelen]\n\t"
"push %[name]\n\t"
"push %%eax\n\t" /* Required dummy value */
"int [=13=]x80\n\t"
"add , %%esp" /* 7*4=28 bytes to remove from stack */
: "+a"(syscallnum) /* error code also returned in syscallnum */
: [name]"r"(name), [namelen]"r"(namelen),
[old]"r"(old) , [oldlenp]"r"(oldlenp),
[new]"i"(NULL), [newlen]"i"(0)
: "memory");
if (syscallnum) {
return EXIT_FAILURE;
}
osname[osnamelen]='[=13=]'; /* Ensure the OS Name is Null terminated */
printf("This machine's node name is %s\n", osname);
return EXIT_SUCCESS;
}
当内联汇编调整 ESP(push
等)时,它可能导致 GCC 生成并通过约束传递的内存操作数指向错误的内存位置。如果任何数据都放在堆栈上,则尤其如此。为避免此问题,最简单的方法是通过寄存器传递地址。
使用 syscall
函数而不是内联汇编也可以这样写:
#include <unistd.h>
#include <sys/syscall.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/sysctl.h>
#define OSNAME_MAX_LEN 256
/* SYS___sysctl has the value 202 */
/* #define SYS___sysctl 202 */
int main(void)
{
char osname[OSNAME_MAX_LEN];
size_t osnamelen = sizeof(osname) - 1;
int name[] = {CTL_KERN, KERN_HOSTNAME};
u_int namelen = sizeof(name) / sizeof(name[0]);
char * old = osname;
size_t * oldlenp = &osnamelen;
if (syscall(SYS___sysctl, name, namelen, old, oldlenp, NULL, 0) == -1) {
perror("sysctl");
return EXIT_FAILURE;
}
osname[osnamelen]='[=14=]'; /* Ensure the OS Name is Null terminated */
printf("This machine's node name is %s\n", osname);
return EXIT_SUCCESS;
}