6

How can I access the system time using NASM, on Linux?

(Editor's note: the accepted answer is for 16-bit DOS with direct hardware access; it would work inside DOSBox. The other answers are actually for Linux.)

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
user176121
  • 710
  • 2
  • 11
  • 22
  • Kind of a big edit but I tried to make the question approximate what I thought the OP was asking.... – Bob Cross Sep 23 '09 at 13:17
  • Can you use system call / `/dev` filesystem? – Ciro Santilli OurBigBook.com Sep 25 '15 at 09:48
  • Working QEMU 16-bit real mode example: https://github.com/cirosantilli/x86-bare-metal-examples/blob/173111e0d0081701512a10d72dc0439f4ab1f55d/in_rtc.S Can you use system call / /dev filesystem? – Ciro Santilli OurBigBook.com Sep 30 '15 at 11:59
  • 1
    Just as an aside, if this number generator is being used cryptographically, this is a terrible idea. Time is not entropic – Restioson Nov 07 '17 at 15:08
  • This question originally mentioned using the time as a seed for an RNG. On Linux, open/read `/dev/urandom` to get entropy, or simply use `rdtsc` if you want some entropy based on time-since-reset and don't care about the actual meaning as time. I edited the question to remove that motivation because future readers finding this question from google probably actually want the time of day. – Peter Cordes May 12 '21 at 17:47

5 Answers5

7

Using 32-bit code under Linux:

mov  eax, 13         ; call number = __NR_time
xor  ebx, ebx        ; tloc = NULL
int  0x80
; 32-bit time_t in EAX

This is a system call to time(2) (system call number 13), and it returns the signed 32-bit time_t in EAX.

(Unlike other system calls, return values >= -4095U (MAX_ERRNO) are still successes, and simply small negative numbers that represent times just before Jan 1, 1970. With a NULL pointer arg, time(2) can't fail. See the man page for details.)

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
jpowell
  • 365
  • 3
  • 5
  • Or just pass NULL and use the time_t that's returned in EAX as the system-call return value. https://man7.org/linux/man-pages/man2/time.2.html says *The `tloc` argument is obsolescent and should always be NULL in new code.* It's pointless to also ask the kernel to store it somewhere else. – Peter Cordes May 12 '21 at 17:52
5

On bare metal (in a custom OS), or in a DOS program:

%define RTCaddress  0x70
%define RTCdata     0x71

;Get time and date from RTC

.l1:    mov al,10           ;Get RTC register A
    out RTCaddress,al
    in al,RTCdata
    test al,0x80            ;Is update in progress?
    jne .l1             ; yes, wait

    mov al,0            ;Get seconds (00 to 59)
    out RTCaddress,al
    in al,RTCdata
    mov [RTCtimeSecond],al

    mov al,0x02         ;Get minutes (00 to 59)
    out RTCaddress,al
    in al,RTCdata
    mov [RTCtimeMinute],al

    mov al,0x04         ;Get hours (see notes)
    out RTCaddress,al
    in al,RTCdata
    mov [RTCtimeHour],al

    mov al,0x07         ;Get day of month (01 to 31)
    out RTCaddress,al
    in al,RTCdata
    mov [RTCtimeDay],al

    mov al,0x08         ;Get month (01 to 12)
    out RTCaddress,al
    in al,RTCdata
    mov [RTCtimeMonth],al

    mov al,0x09         ;Get year (00 to 99)
    out RTCaddress,al
    in al,RTCdata
    mov [RTCtimeYear],al

    ret

This uses NASM, and is from here.

This will not work under a normal OS like Linux that stops user-space processes from directly accessing hardware. You could maybe get this to work as root, with an ioperm(2) system call to allow access to that I/O port. Linux only updates the BIOS/hardware RTC to match the current system time during shutdown, not continuously, so don't expect it to be perfectly in sync, especially if the motherboard battery is dead.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Kyle Rosendo
  • 25,001
  • 7
  • 80
  • 118
  • 1
    I'm a little confused. Is this working on the assumption that the poster is using windows? – BigBeagle Sep 23 '09 at 13:23
  • Should work for any OS on x86 that maintains its system date/time. – Kyle Rosendo Sep 23 '09 at 13:27
  • Just tried -- Unfortunately, "out RTCaddress,al" fails on Vista64 because of privilege level. – PhiS Sep 23 '09 at 21:09
  • 1
    Ah, I don't really know then. As I said, I this should work on x86, not x64, let alone with Vista's glorious security, hehe. – Kyle Rosendo Sep 24 '09 at 17:01
  • 1
    This will not work under any Unix style system, including Linux, which is what the OP asked for. You cannot access the hardware ports directly outside of the kernel but must instead use the `int 0x80` interface to get there. – David Hoelzer Jun 22 '15 at 15:36
5

With NASM, if you are targeting Linux x86-64, you can simply do the following:

mov rax, 201
xor rdi, rdi        
syscall

201 corresponds to the 64-bit system call number for sys_time (as listed here). Register rdi is set to 0 so the return value after performing the system call is stored in rax, but you could also make it point to a memory location of your choosing. The result is expressed in seconds since the Epoch.

More information about this system call can be found in the time man page.

Pyves
  • 6,333
  • 7
  • 41
  • 59
2

As an addendum to the answer by Pyves above (using x86-64/NASM/Linux), if you want to get better clock resolution than a second, you can syscall with 228 instead of 201 to get seconds in one 64-bit variable and additional nanoseconds (beyond the seconds) in another 64-bit variable.

default rel
section .bss
    time: resq 2   ; 2 qwords for seconds and nanoseconds

section .text
    mov rax, 228   ; 228 is system call for sys_clock_gettime
    xor edi, edi   ; 0 for system clock (preferred over "mov rdi, 0")
    lea rsi, [time]
    syscall        ; [time] contains number of seconds
                   ; [time + 8] contains number of nanoseconds

From the man page, the system call is
int clock_gettime(clockid_t clock_id, struct timespec *tp);
struct timespec on x86-64 is a pair of unsigned 64-bit integers, with seconds at the low address, nanos at the higher address.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
John Moore
  • 7,282
  • 3
  • 14
  • 14
  • Note that calling the glibc wrapper function (or [calling into the VDSO directly](https://blog.packagecloud.io/eng/2016/04/05/the-definitive-guide-to-linux-system-calls/#virtual-system-calls)) will be faster than using `syscall` - the kernel exports code + data for a user-space implementation of the function using rdtsc to interpolate since the last `CLOCK_MONOTONIC_COARSE` tick without ever calling into the kernel. But if you just want something simple for hand-written asm, then yes, do this. – Peter Cordes May 12 '21 at 17:49
0

I'd say, depending on what platform you're on, you'll have to use an OS function.

On windows, try GetSystemTime. On linux, try gettimeofday - see related question here.

Community
  • 1
  • 1
PhiS
  • 4,540
  • 25
  • 35