В этом ответе предполагается, что есть причина, по которой вы хотите использовать системные вызовы напрямую, а не через 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 $0x80\n\t"
"add $8, %%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;
}
При 32-битных системных вызовах FreeBSD параметры помещаются в стек в обратном порядке. Фиктивное значение (любое значение) должно быть помещено в стек перед выдачей int $0x80
. Вам нужно настроить указатель стека ESP после системного вызова. Любые регистры, которые могут измениться, также должны быть обработаны. int $0x80
вернет код ошибки в EAX . Приведенный выше код возвращает это значение обратно в переменную syscallnum
. Если вы изменяете регистр во встроенной сборке и не сообщаете GCC, это может привести к неопределенному поведению, которое часто трудно отследить.
Если вы передаете адреса через регистры, вам нужно будет добавить операнды памяти (даже если они являются фиктивными), чтобы указать, что данные в указателе в регистрах читаются и / или записываются. В качестве альтернативы вы можете указать memory
clobber, который может быть проще для понимания, хотя это более грубый подход.
Встроенная сборка GCC является мощной, но ее трудно понять правильно и может вызвать неожиданное поведение, если вы ошибаетесь. Вы должны использовать только встроенную сборку как последнее средство . FreeBSD имеет функцию syscall
, которую можно использовать для вызова большинства системных вызовов.
Вы могли бы написать встроенную сборку выше как:
asm(
"push %[stroj]\n\t"
"push %%eax\n\t" /* Required dummy value for int 0x80 */
"int $0x80\n\t"
"add $8, %%esp" /* 2*4 bytes removed from stack */
: "+a"(syscallnum), /* error code also returned in syscallnum */
"=m"(stroj)
: [stroj]"r"(&stroj));
FreeBSD 2+ не поддерживает устаревшее SYS_uname
Если вы попытаетесь запустить приведенный выше код, вы обнаружите, что он ничего не возвращает. Если вы используете программу TRUSS с такой командой, как truss ./progname
, вы должны увидеть что-то подобное в выводе:
obs_uname (0xffffc6f8,0x0,0x0,0x0,0x0,0x0) ERR # 78 'Функция не реализована'
Это связано с тем, что FreeBSD 2+ не поддерживает системный вызов SYS_uname
и теперь считается устаревшим. Libc uname
из FreeBSD вызывает 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 $0x80\n\t"
"add $28, %%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]='\0'; /* 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]='\0'; /* Ensure the OS Name is Null terminated */
printf("This machine's node name is %s\n", osname);
return EXIT_SUCCESS;
}