From c90fa2ace73851d6af4946c239c90db91cad8abe Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Tue, 9 Jul 2013 02:23:16 -0400 Subject: [PATCH 01/16] add realtime signals to strsignal the name format RTnn/RTnnn was chosen to minimized bloat while uniquely identifying the signal. --- src/string/strsignal.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/string/strsignal.c b/src/string/strsignal.c index d70982aa..f0c3db7a 100644 --- a/src/string/strsignal.c +++ b/src/string/strsignal.c @@ -48,7 +48,7 @@ static const char map[] = { [SIGSYS] = 31 }; -#define sigmap(x) ((unsigned)(x) >= sizeof map ? 0 : map[(unsigned)(x)]) +#define sigmap(x) ((x) >= sizeof map ? (x) : map[(x)]) #endif @@ -84,14 +84,30 @@ static const char strings[] = "Window changed\0" "I/O possible\0" "Power failure\0" - "Bad system call"; + "Bad system call\0" + "RT32" + "\0RT33\0RT34\0RT35\0RT36\0RT37\0RT38\0RT39\0RT40" + "\0RT41\0RT42\0RT43\0RT44\0RT45\0RT46\0RT47\0RT48" + "\0RT49\0RT50\0RT51\0RT52\0RT53\0RT54\0RT55\0RT56" + "\0RT57\0RT58\0RT59\0RT60\0RT61\0RT62\0RT63\0RT64" +#if _NSIG > 65 + "\0RT65\0RT66\0RT67\0RT68\0RT69\0RT70\0RT71\0RT72" + "\0RT73\0RT74\0RT75\0RT76\0RT77\0RT78\0RT79\0RT80" + "\0RT81\0RT82\0RT83\0RT84\0RT85\0RT86\0RT87\0RT88" + "\0RT89\0RT90\0RT91\0RT92\0RT93\0RT94\0RT95\0RT96" + "\0RT97\0RT98\0RT99\0RT100\0RT101\0RT102\0RT103\0RT104" + "\0RT105\0RT106\0RT107\0RT108\0RT109\0RT110\0RT111\0RT112" + "\0RT113\0RT114\0RT115\0RT116\0RT117\0RT118\0RT119\0RT120" + "\0RT121\0RT122\0RT123\0RT124\0RT125\0RT126\0RT127\0RT128" +#endif + ""; char *strsignal(int signum) { char *s = (char *)strings; signum = sigmap(signum); - if ((unsigned)signum - 1 > 31) signum = 0; + if (signum - 1U >= _NSIG-1) signum = 0; for (; signum--; s++) for (; *s; s++); -- 2.20.1 From c713d8797804903b54203a645e023e2077c7556d Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Tue, 9 Jul 2013 02:30:21 -0400 Subject: [PATCH 02/16] fix a couple misleading/wrong signal descriptions in strsignal there are still several more that are misleading, but SIGFPE (integer division error misdescribed as floating point) and and SIGCHLD (possibly non-exit status change events described as exiting) were the worst offenders. --- src/string/strsignal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/string/strsignal.c b/src/string/strsignal.c index f0c3db7a..905c0956 100644 --- a/src/string/strsignal.c +++ b/src/string/strsignal.c @@ -61,7 +61,7 @@ static const char strings[] = "Trace/breakpoint trap\0" "Aborted\0" "Bus error\0" - "Floating point exception\0" + "Arithmetic exception\0" "Killed\0" "User defined signal 1\0" "Segmentation fault\0" @@ -70,7 +70,7 @@ static const char strings[] = "Alarm clock\0" "Terminated\0" "Stack fault\0" - "Child exited\0" + "Child process status\0" "Continued\0" "Stopped (signal)\0" "Stopped\0" -- 2.20.1 From 30763fd01bef85f30e79baa30173674c007690cc Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Wed, 10 Jul 2013 14:38:20 -0400 Subject: [PATCH 03/16] fix invalid library phdr pointers passed to callback from dl_iterate_phdr map_library was saving pointers to an automatic-storage buffer rather than pointers into the mapping. this should be a fairly simple fix, but the patch here is slightly complicated by two issues: 1. supporting gratuitously obfuscated ELF files where the program headers are not right at the beginning of the file. 2. cleaning up the map_library function so that data isn't clobbered by the time we need it. --- src/ldso/dynlink.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/ldso/dynlink.c b/src/ldso/dynlink.c index 7031d03a..ff5b738d 100644 --- a/src/ldso/dynlink.c +++ b/src/ldso/dynlink.c @@ -309,7 +309,7 @@ static void *map_library(int fd, struct dso *dso) size_t this_min, this_max; off_t off_start; Ehdr *eh; - Phdr *ph; + Phdr *ph, *ph0; unsigned prot; unsigned char *map, *base; size_t dyn; @@ -324,11 +324,10 @@ static void *map_library(int fd, struct dso *dso) if (eh->e_phoff + phsize > l) { l = pread(fd, buf+1, phsize, eh->e_phoff); if (l != phsize) return 0; - eh->e_phoff = sizeof *eh; + ph = ph0 = (void *)(buf + 1); + } else { + ph = ph0 = (void *)((char *)buf + eh->e_phoff); } - ph = (void *)((char *)buf + eh->e_phoff); - dso->phdr = ph; - dso->phnum = eh->e_phnum; for (i=eh->e_phnum; i; i--, ph=(void *)((char *)ph+eh->e_phentsize)) { if (ph->p_type == PT_DYNAMIC) dyn = ph->p_vaddr; @@ -363,9 +362,18 @@ static void *map_library(int fd, struct dso *dso) map = mmap((void *)addr_min, map_len, prot, MAP_PRIVATE, fd, off_start); if (map==MAP_FAILED) return 0; base = map - addr_min; - ph = (void *)((char *)buf + eh->e_phoff); - for (i=eh->e_phnum; i; i--, ph=(void *)((char *)ph+eh->e_phentsize)) { + dso->phdr = 0; + dso->phnum = 0; + for (ph=ph0, i=eh->e_phnum; i; i--, ph=(void *)((char *)ph+eh->e_phentsize)) { if (ph->p_type != PT_LOAD) continue; + /* Check if the programs headers are in this load segment, and + * if so, record the address for use by dl_iterate_phdr. */ + if (!dso->phdr && eh->e_phoff >= ph->p_offset + && eh->e_phoff+phsize <= ph->p_offset+ph->p_filesz) { + dso->phdr = (void *)(base + ph->p_vaddr + + (eh->e_phoff-ph->p_offset)); + dso->phnum = eh->e_phnum; + } /* Reuse the existing mapping for the lowest-address LOAD */ if ((ph->p_vaddr & -PAGE_SIZE) == addr_min) continue; this_min = ph->p_vaddr & -PAGE_SIZE; @@ -390,8 +398,7 @@ static void *map_library(int fd, struct dso *dso) goto error; break; } - if (!runtime) reclaim_gaps(base, (void *)((char *)buf + eh->e_phoff), - eh->e_phentsize, eh->e_phnum); + if (!runtime) reclaim_gaps(base, ph0, eh->e_phentsize, eh->e_phnum); dso->map = map; dso->map_len = map_len; dso->base = base; -- 2.20.1 From 3e8806b4f7f25f7c249f61f597d3a113f531a059 Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Wed, 10 Jul 2013 16:11:01 -0400 Subject: [PATCH 04/16] add some ARM EABI-specific exception handling infrastructure MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit patch by Timo Teräs --- arch/arm/src/find_exidx.c | 42 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 arch/arm/src/find_exidx.c diff --git a/arch/arm/src/find_exidx.c b/arch/arm/src/find_exidx.c new file mode 100644 index 00000000..77c4472b --- /dev/null +++ b/arch/arm/src/find_exidx.c @@ -0,0 +1,42 @@ +#define _GNU_SOURCE +#include +#include + +struct find_exidx_data { + uintptr_t pc, exidx_start; + int exidx_len; +}; + +static int find_exidx(struct dl_phdr_info *info, size_t size, void *ptr) +{ + struct find_exidx_data *data = ptr; + const ElfW(Phdr) *phdr = info->dlpi_phdr; + uintptr_t addr, exidx_start = 0; + int i, match = 0, exidx_len = 0; + + for (i = info->dlpi_phnum; i > 0; i--, phdr++) { + addr = info->dlpi_addr + phdr->p_vaddr; + switch (phdr->p_type) { + case PT_LOAD: + match |= data->pc >= addr && data->pc < addr + phdr->p_memsz; + break; + case PT_ARM_EXIDX: + exidx_start = addr; + exidx_len = phdr->p_memsz; + break; + } + } + data->exidx_start = exidx_start; + data->exidx_len = exidx_len; + return match; +} + +uintptr_t __gnu_Unwind_Find_exidx(uintptr_t pc, int *pcount) +{ + struct find_exidx_data data; + data.pc = pc; + if (dl_iterate_phdr(find_exidx, &data) <= 0) + return 0; + *pcount = data.exidx_len / 8; + return data.exidx_start; +} -- 2.20.1 From c07da2e9704d2ba95efa34a4fde2747d973b4e4c Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Wed, 10 Jul 2013 16:58:27 -0400 Subject: [PATCH 05/16] add PIE support for ARM --- crt/arm/Scrt1.s | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 crt/arm/Scrt1.s diff --git a/crt/arm/Scrt1.s b/crt/arm/Scrt1.s new file mode 100644 index 00000000..a54859aa --- /dev/null +++ b/crt/arm/Scrt1.s @@ -0,0 +1,30 @@ +.weak _init +.weak _fini +.global _start +_start: + mov fp,#0 + mov lr,#0 + + ldr a2,[sp],#4 + mov a3,sp + str fp,[sp,#-4]! + str a1,[sp,#-4]! + + adr ip,2f + ldr a4,2f+4 + add a4,a4,ip + str a4,[sp,#-4]! + ldr a4,2f+8 + add a4,a4,ip + + ldr a1,2f + add ip,ip,a1 + ldr a1,2f+12 + ldr a1,[ip,a1] + + bl __libc_start_main(PLT) +1: b 1b +2: .word _GLOBAL_OFFSET_TABLE_-2b + .word _fini-2b + .word _init-2b + .word main(GOT) -- 2.20.1 From f1292e3d28309bbc81f61671164843cec4319bfa Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Sat, 13 Jul 2013 14:54:34 -0400 Subject: [PATCH 06/16] fix omission of dtv setup in static linked programs on TLS variant I archs apparently this was never noticed before because the linker normally optimizes dynamic TLS models to non-dynamic ones when static linking, thus eliminating the calls to __tls_get_addr which crash when the dtv is missing. however, some libsupc++ code on ARM was calling __tls_get_addr when static linked and crashing. the reason is unclear to me, but with this issue fixed it should work now anyway. --- src/env/__init_tls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/env/__init_tls.c b/src/env/__init_tls.c index eb2a6e4a..a4704f06 100644 --- a/src/env/__init_tls.c +++ b/src/env/__init_tls.c @@ -26,9 +26,9 @@ void *__copy_tls(unsigned char *mem) mem += libc.tls_size - sizeof(struct pthread); mem -= (uintptr_t)mem & (align-1); td = (pthread_t)mem; - td->dtv = dtv; mem -= size; #endif + td->dtv = dtv; dtv[1] = mem; memcpy(mem, image, len); return td; -- 2.20.1 From 1cc81f5cb0df2b66a795ff0c26d7bbc4d16e13c6 Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Wed, 17 Jul 2013 05:24:50 -0400 Subject: [PATCH 07/16] the big time handling overhaul this commit has two major user-visible parts: zoneinfo-format time zones are now supported, and overflow handling is intended to be complete in the sense that all functions return a correct result if and only if the result fits in the destination type, and otherwise return an error. also, some noticable bugs in the way DST detection and normalization worked have been fixed, and performance may be better than before, but it has not been tested. --- src/time/__map_file.c | 20 ++ src/time/__month_to_secs.c | 10 + src/time/__secs_to_tm.c | 81 ++++++++ src/time/__time.h | 9 - src/time/__time_to_tm.c | 83 -------- src/time/__tm_to_secs.c | 24 +++ src/time/__tm_to_time.c | 33 ---- src/time/__tz.c | 389 +++++++++++++++++++++++++++++++++++++ src/time/__year_to_secs.c | 47 +++++ src/time/gmtime.c | 9 +- src/time/gmtime_r.c | 21 +- src/time/localtime.c | 9 +- src/time/localtime_r.c | 20 +- src/time/mktime.c | 36 ++-- src/time/strftime.c | 8 +- src/time/time_impl.h | 9 + src/time/timegm.c | 17 +- src/time/tzset.c | 172 ---------------- 18 files changed, 649 insertions(+), 348 deletions(-) create mode 100644 src/time/__map_file.c create mode 100644 src/time/__month_to_secs.c create mode 100644 src/time/__secs_to_tm.c delete mode 100644 src/time/__time.h delete mode 100644 src/time/__time_to_tm.c create mode 100644 src/time/__tm_to_secs.c delete mode 100644 src/time/__tm_to_time.c create mode 100644 src/time/__tz.c create mode 100644 src/time/__year_to_secs.c create mode 100644 src/time/time_impl.h delete mode 100644 src/time/tzset.c diff --git a/src/time/__map_file.c b/src/time/__map_file.c new file mode 100644 index 00000000..b6bf272a --- /dev/null +++ b/src/time/__map_file.c @@ -0,0 +1,20 @@ +#include +#include +#include +#include "syscall.h" + +void *__mmap(void *, size_t, int, int, int, off_t); + +const char unsigned *__map_file(const char *pathname, size_t *size) +{ + struct stat st; + const unsigned char *map = MAP_FAILED; + int flags = O_RDONLY|O_LARGEFILE|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK; + int fd = __syscall(SYS_open, pathname, flags); + if (fd < 0) return 0; + if (!__syscall(SYS_fstat, fd, &st)) + map = __mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + __syscall(SYS_close); + *size = st.st_size; + return map == MAP_FAILED ? 0 : map; +} diff --git a/src/time/__month_to_secs.c b/src/time/__month_to_secs.c new file mode 100644 index 00000000..43248fb3 --- /dev/null +++ b/src/time/__month_to_secs.c @@ -0,0 +1,10 @@ +int __month_to_secs(int month, int is_leap) +{ + static const int secs_through_month[] = { + 0, 31*86400, 59*86400, 90*86400, + 120*86400, 151*86400, 181*86400, 212*86400, + 243*86400, 273*86400, 304*86400, 334*86400 }; + int t = secs_through_month[month]; + if (is_leap && month >= 2) t+=86400; + return t; +} diff --git a/src/time/__secs_to_tm.c b/src/time/__secs_to_tm.c new file mode 100644 index 00000000..f3c1cf92 --- /dev/null +++ b/src/time/__secs_to_tm.c @@ -0,0 +1,81 @@ +#include "time_impl.h" +#include + +/* 2000-03-01 (mod 400 year, immediately after feb29 */ +#define LEAPOCH (946684800LL + 86400*(31+29)) + +#define DAYS_PER_400Y (365*400 + 97) +#define DAYS_PER_100Y (365*100 + 24) +#define DAYS_PER_4Y (365*4 + 1) + +int __secs_to_tm(long long t, struct tm *tm) +{ + long long days, secs; + int remdays, remsecs, remyears; + int qc_cycles, c_cycles, q_cycles; + int years, months; + int wday, yday, leap; + static const char days_in_month[] = {31,30,31,30,31,31,30,31,30,31,31,29}; + + /* Reject time_t values whose year would overflow int */ + if (t < INT_MIN * 31622400LL || t > INT_MAX * 31622400LL) + return -1; + + secs = t - LEAPOCH; + days = secs / 86400; + remsecs = secs % 86400; + if (remsecs < 0) { + remsecs += 86400; + days--; + } + + wday = (3+days)%7; + if (wday < 0) wday += 7; + + qc_cycles = days / DAYS_PER_400Y; + remdays = days % DAYS_PER_400Y; + if (remdays < 0) { + remdays += DAYS_PER_400Y; + qc_cycles--; + } + + c_cycles = remdays / DAYS_PER_100Y; + if (c_cycles == 4) c_cycles--; + remdays -= c_cycles * DAYS_PER_100Y; + + q_cycles = remdays / DAYS_PER_4Y; + if (q_cycles == 25) q_cycles--; + remdays -= q_cycles * DAYS_PER_4Y; + + remyears = remdays / 365; + if (remyears == 4) remyears--; + remdays -= remyears * 365; + + leap = !remyears && (q_cycles || !c_cycles); + yday = remdays + 31 + 28 + leap; + if (yday >= 365+leap) yday -= 365+leap; + + years = remyears + 4*q_cycles + 100*c_cycles + 400*qc_cycles; + + for (months=0; days_in_month[months] <= remdays; months++) + remdays -= days_in_month[months]; + + if (years+100 > INT_MAX || years+100 < INT_MIN) + return -1; + + tm->tm_year = years + 100; + tm->tm_mon = months + 2; + if (tm->tm_mon >= 12) { + tm->tm_mon -=12; + tm->tm_year++; + } + tm->tm_mday = remdays + 1; + tm->tm_wday = wday; + tm->tm_yday = yday; + + tm->tm_hour = remsecs / 3600; + tm->tm_min = remsecs / 60 % 60; + tm->tm_sec = remsecs % 60; + + return 0; +} diff --git a/src/time/__time.h b/src/time/__time.h deleted file mode 100644 index 967e5180..00000000 --- a/src/time/__time.h +++ /dev/null @@ -1,9 +0,0 @@ -time_t __tm_to_time(struct tm *); -struct tm *__time_to_tm(time_t, struct tm *); -void __tzset(void); -struct tm *__dst_adjust(struct tm *tm); - -extern long __timezone; -extern int __daylight; -extern int __dst_offset; -extern char *__tzname[2]; diff --git a/src/time/__time_to_tm.c b/src/time/__time_to_tm.c deleted file mode 100644 index e2d782be..00000000 --- a/src/time/__time_to_tm.c +++ /dev/null @@ -1,83 +0,0 @@ -#include - -/* C defines the rounding for division in a nonsensical way */ -#define Q(a,b) ((a)>0 ? (a)/(b) : -(((b)-(a)-1)/(b))) - -#define DAYS_PER_400Y (365*400 + 97) -#define DAYS_PER_100Y (365*100 + 24) -#define DAYS_PER_4Y (365*4 + 1) - -/* FIXME: use lldiv once it's fixed to compute quot,rem together */ -struct tm *__time_to_tm(time_t t, struct tm *tm) -{ - /* months are march-based */ - static const int days_thru_month[] = {31,61,92,122,153,184,214,245,275,306,337,366}; - long long bigday; - unsigned int day, year4, year100; - int year, year400; - int month; - int leap; - int hour, min, sec; - int wday, mday, yday; - - /* start from 2000-03-01 (multiple of 400 years) */ - t += -946684800 - 86400*(31+29); - - bigday = Q(t, 86400); - sec = t-bigday*86400; - - hour = sec/3600; - sec -= hour*3600; - min = sec/60; - sec -= min*60; - - /* 2000-03-01 was a wednesday */ - wday = (3+bigday)%7; - if (wday < 0) wday += 7; - - t = -946684800LL - 86400*(31+29) + 9000000; - - year400 = Q(bigday, DAYS_PER_400Y); - day = bigday-year400*DAYS_PER_400Y; - - year100 = day/DAYS_PER_100Y; - if (year100 == 4) year100--; - day -= year100*DAYS_PER_100Y; - - year4 = day/DAYS_PER_4Y; - if (year4 == 25) year4--; - day -= year4*DAYS_PER_4Y; - - year = day/365; - if (year == 4) year--; - day -= year*365; - - leap = !year && (year4 || !year100); - yday = day + 31+28 + leap; - if (yday >= 365+leap) yday -= 365+leap; - - year += 4*year4 + 100*year100 + 400*year400 + 2000-1900; - - for (month=0; days_thru_month[month] <= day; month++); - if (month) day -= days_thru_month[month-1]; - month += 2; - if (month >= 12) { - month -= 12; - year++; - } - - mday = day+1; - - tm->tm_sec = sec; - tm->tm_min = min; - tm->tm_hour= hour; - tm->tm_mday= mday; - tm->tm_mon = month; - tm->tm_year= year; - tm->tm_wday= wday; - tm->tm_yday= yday; - tm->__tm_zone = 0; - tm->__tm_gmtoff = 0; - - return tm; -} diff --git a/src/time/__tm_to_secs.c b/src/time/__tm_to_secs.c new file mode 100644 index 00000000..c29fa985 --- /dev/null +++ b/src/time/__tm_to_secs.c @@ -0,0 +1,24 @@ +#include "time_impl.h" + +long long __tm_to_secs(const struct tm *tm) +{ + int is_leap; + long long year = tm->tm_year; + int month = tm->tm_mon; + if (month >= 12 || month < 0) { + int adj = month / 12; + month %= 12; + if (month < 0) { + adj--; + month += 12; + } + year += adj; + } + long long t = __year_to_secs(year, &is_leap); + t += __month_to_secs(month, is_leap); + t += 86400LL * (tm->tm_mday-1); + t += 3600LL * tm->tm_hour; + t += 60LL * tm->tm_min; + t += tm->tm_sec; + return t; +} diff --git a/src/time/__tm_to_time.c b/src/time/__tm_to_time.c deleted file mode 100644 index 9f11805d..00000000 --- a/src/time/__tm_to_time.c +++ /dev/null @@ -1,33 +0,0 @@ -#include - -/* C defines the rounding for division in a nonsensical way */ -#define Q(a,b) ((a)>0 ? (a)/(b) : -(((b)-(a)-1)/(b))) - -time_t __tm_to_time(struct tm *tm) -{ - time_t year = tm->tm_year + -100; - int month = tm->tm_mon; - int day = tm->tm_mday; - int z4, z100, z400; - - /* normalize month */ - if (month >= 12) { - year += month/12; - month %= 12; - } else if (month < 0) { - year += month/12; - month %= 12; - if (month) { - month += 12; - year--; - } - } - z4 = Q(year - (month < 2), 4); - z100 = Q(z4, 25); - z400 = Q(z100, 4); - day += year*365 + z4 - z100 + z400 + - month[(const int []){0,31,59,90,120,151,181,212,243,273,304,334}]; - return (long long)day*86400 - + tm->tm_hour*3600 + tm->tm_min*60 + tm->tm_sec - - -946684800; /* the dawn of time :) */ -} diff --git a/src/time/__tz.c b/src/time/__tz.c new file mode 100644 index 00000000..a76a7b48 --- /dev/null +++ b/src/time/__tz.c @@ -0,0 +1,389 @@ +#include "time_impl.h" +#include +#include +#include +#include +#include "libc.h" + +long __timezone = 0; +int __daylight = 0; +char *__tzname[2] = { 0, 0 }; + +weak_alias(__timezone, timezone); +weak_alias(__daylight, daylight); +weak_alias(__tzname, tzname); + +static char std_name[TZNAME_MAX+1]; +static char dst_name[TZNAME_MAX+1]; + +static int dst_off; +static int r0[5], r1[5]; + +static const unsigned char *zi, *trans, *index, *types, *abbrevs; +static size_t map_size; + +static char old_tz_buf[32]; +static char *old_tz = old_tz_buf; +static size_t old_tz_size = sizeof old_tz_buf; + +static int lock[2]; + +static int getint(const char **p) +{ + unsigned x; + for (x=0; **p-'0'<10U; (*p)++) x = **p-'0' + 10*x; + return x; +} + +static int getsigned(const char **p) +{ + if (**p == '-') { + ++*p; + return -getint(p); + } + if (**p == '+') ++*p; + return getint(p); +} + +static int getoff(const char **p) +{ + int off = 3600*getsigned(p); + if (**p == ':') { + ++*p; + off += 60*getint(p); + if (**p == ':') { + ++*p; + off += getint(p); + } + } + return off; +} + +static void getrule(const char **p, int rule[5]) +{ + int r = rule[0] = **p; + + if (r!='M') { + if (r=='J') ++*p; + else rule[0] = 0; + rule[1] = getint(p); + } else { + ++*p; rule[1] = getint(p); + ++*p; rule[2] = getint(p); + ++*p; rule[3] = getint(p); + } + + if (**p=='/') { + ++*p; + rule[4] = getoff(p); + } else { + rule[4] = 7200; + } +} + +static void getname(char *d, const char **p) +{ + int i; + if (**p == '<') { + ++*p; + for (i=0; **p!='>' && i PATH_MAX+1) s = "", i = 0; + if (i >= old_tz_size) { + old_tz_size *= 2; + if (i >= old_tz_size) old_tz_size = i+1; + if (old_tz_size > PATH_MAX+2) old_tz_size = PATH_MAX+2; + old_tz = malloc(old_tz_size); + } + if (old_tz) memcpy(old_tz, s, i+1); + + if (*s == ':') s++; + + /* Non-suid can use an absolute tzfile pathname or a relative + * pathame beginning with "."; in secure mode, only the + * standard path will be searched. */ + if (*s == '/' || *s == '.') { + if (!libc.secure) map = __map_file(s, &map_size); + } else { + for (i=0; s[i] && s[i]!=','; i++) { + if (s[i]=='/') { + size_t l = strlen(s); + if (l > NAME_MAX || strchr(s, '.')) + break; + memcpy(pathname, s, l+1); + pathname[l] = 0; + for (try=search; !map && *try; try+=l) { + l = strlen(try); + memcpy(pathname-l, try, l); + map = __map_file(pathname-l, &map_size); + } + break; + } + } + } + + zi = map; + if (map) { + int scale = 2; + if (sizeof(time_t) > 4 && map[4]=='2') { + size_t skip = zi_dotprod(zi, VEC(1,1,8,5,6,1), 6); + trans = zi+skip+44+20; + scale++; + } else { + trans = zi+44; + } + index = trans + (zi_read32(trans-12) << scale); + types = index + zi_read32(trans-12); + abbrevs = types + 6*zi_read32(trans-8); + if (zi[map_size-1] == '\n') { + for (s = (const char *)zi+map_size-2; *s!='\n'; s--); + s++; + } else { + s = 0; + } + } + + if (!s) s = "GMT0"; + getname(std_name, &s); + __tzname[0] = std_name; + __timezone = getoff(&s); + getname(dst_name, &s); + __tzname[1] = dst_name; + if (dst_name[0]) { + __daylight = 1; + if (*s == '+' || *s=='-' || *s-'0'<10U) + dst_off = getoff(&s); + else + dst_off = __timezone - 3600; + } else { + __daylight = 0; + dst_off = 0; + } + + if (*s == ',') s++, getrule(&s, r0); + if (*s == ',') s++, getrule(&s, r1); +} + +/* Search zoneinfo rules to find the one that applies to the given time, + * and determine alternate opposite-DST-status rule that may be needed. */ + +static size_t scan_trans(long long t, int local, size_t *alt) +{ + int scale = 3 - (trans == zi+44); + uint64_t x; + int off = 0; + + size_t a = 0, n = (index-trans)>>scale, m; + + if (!n) { + if (alt) *alt = 0; + return 0; + } + + /* Binary search for 'most-recent rule before t'. */ + while (n > 1) { + m = a + n/2; + x = zi_read32(trans + (m<>scale; + if (a == n-1) return -1; + if (a == 0) { + x = zi_read32(trans + (a<>(m-1))&1); +} + +/* Convert a POSIX DST rule plus year to seconds since epoch. */ + +static long long rule_to_secs(const int *rule, int year) +{ + int is_leap; + long long t = __year_to_secs(year, &is_leap); + int x, m, n, d; + if (rule[0]!='M') { + x = rule[1]; + if (rule[0]=='J' && (x < 60 || !is_leap)) x--; + t += 86400 * x; + } else { + m = rule[1]; + n = rule[2]; + d = rule[3]; + t += __month_to_secs(m-1, is_leap); + int wday = (int)((t + 4*86400) % (7*86400)) / 86400; + int days = d - wday; + if (days < 0) days += 7; + if (n == 5 && days+28 >= days_in_month(m, is_leap)) n = 4; + t += 86400 * (days + 7*(n-1)); + } + t += rule[4]; + return t; +} + +/* Determine the time zone in effect for a given time in seconds since the + * epoch. It can be given in local or universal time. The results will + * indicate whether DST is in effect at the queried time, and will give both + * the GMT offset for the active zone/DST rule and the opposite DST. This + * enables a caller to efficiently adjust for the case where an explicit + * DST specification mismatches what would be in effect at the time. */ + +void __secs_to_zone(long long t, int local, int *isdst, long *offset, long *oppoff, const char **zonename) +{ + LOCK(lock); + + do_tzset(); + + if (zi) { + size_t alt, i = scan_trans(t, local, &alt); + if (i != -1) { + *isdst = types[6*i+4]; + *offset = -(int32_t)zi_read32(types+6*i); + *zonename = (const char *)abbrevs + types[6*i+5]; + if (oppoff) *oppoff = -(int32_t)zi_read32(types+6*alt); + UNLOCK(lock); + return; + } + } + + if (!__daylight) goto std; + + /* FIXME: may be broken if DST changes right at year boundary? + * Also, this could be more efficient.*/ + long long y = t / 31556952 + 70; + while (__year_to_secs(y, 0) > t) y--; + while (__year_to_secs(y+1, 0) < t) y++; + + long long t0 = rule_to_secs(r0, y); + long long t1 = rule_to_secs(r1, y); + + if (t0 < t1) { + if (!local) { + t0 += __timezone; + t1 += dst_off; + } + if (t >= t0 && t < t1) goto dst; + goto std; + } else { + if (!local) { + t1 += __timezone; + t0 += dst_off; + } + if (t >= t1 && t < t0) goto std; + goto dst; + } +std: + *isdst = 0; + *offset = __timezone; + if (oppoff) *oppoff = dst_off; + *zonename = __tzname[0]; + UNLOCK(lock); + return; +dst: + *isdst = 1; + *offset = dst_off; + if (oppoff) *oppoff = __timezone; + *zonename = __tzname[1]; + UNLOCK(lock); +} + +void __tzset() +{ + LOCK(lock); + do_tzset(); + UNLOCK(lock); +} + +weak_alias(__tzset, tzset); diff --git a/src/time/__year_to_secs.c b/src/time/__year_to_secs.c new file mode 100644 index 00000000..2824ec6d --- /dev/null +++ b/src/time/__year_to_secs.c @@ -0,0 +1,47 @@ +long long __year_to_secs(long long year, int *is_leap) +{ + if (year-2ULL <= 136) { + int y = year; + int leaps = (y-68)>>2; + if (!((y-68)&3)) { + leaps--; + if (is_leap) *is_leap = 1; + } else if (is_leap) *is_leap = 0; + return 31536000*(y-70) + 86400*leaps; + } + + int cycles, centuries, leaps, rem; + + if (!is_leap) is_leap = &(int){0}; + cycles = (year-100) / 400; + rem = (year-100) % 400; + if (rem < 0) { + cycles--; + rem += 400; + } + if (!rem) { + *is_leap = 1; + centuries = 0; + leaps = 0; + } else { + if (rem >= 200) { + if (rem >= 300) centuries = 3, rem -= 300; + else centuries = 2, rem -= 200; + } else { + if (rem >= 100) centuries = 1, rem -= 100; + else centuries = 0; + } + if (!rem) { + *is_leap = 0; + leaps = 0; + } else { + leaps = rem / 4U; + rem %= 4U; + *is_leap = !rem; + } + } + + leaps += 97*cycles + 24*centuries - *is_leap; + + return (year-100) * 31536000LL + leaps * 86400LL + 946684800 + 86400; +} diff --git a/src/time/gmtime.c b/src/time/gmtime.c index d4d5d1f1..3791b24c 100644 --- a/src/time/gmtime.c +++ b/src/time/gmtime.c @@ -1,11 +1,10 @@ -#include +#include "time_impl.h" +#include -#include "__time.h" +struct tm *__gmtime_r(const time_t *restrict, struct tm *restrict); struct tm *gmtime(const time_t *t) { static struct tm tm; - __time_to_tm(*t, &tm); - tm.tm_isdst = 0; - return &tm; + return __gmtime_r(t, &tm); } diff --git a/src/time/gmtime_r.c b/src/time/gmtime_r.c index 13a2548f..0272870d 100644 --- a/src/time/gmtime_r.c +++ b/src/time/gmtime_r.c @@ -1,10 +1,17 @@ -#include +#include "time_impl.h" +#include +#include "libc.h" -#include "__time.h" - -struct tm *gmtime_r(const time_t *restrict t, struct tm *restrict result) +struct tm *__gmtime_r(const time_t *restrict t, struct tm *restrict tm) { - __time_to_tm(*t, result); - result->tm_isdst = 0; - return result; + if (__secs_to_tm(*t, tm) < 0) { + errno = EINVAL; + return 0; + } + tm->tm_isdst = 0; + tm->__tm_gmtoff = 0; + tm->__tm_zone = "GMT"; + return tm; } + +weak_alias(__gmtime_r, gmtime_r); diff --git a/src/time/localtime.c b/src/time/localtime.c index abd5e84d..bb6718c3 100644 --- a/src/time/localtime.c +++ b/src/time/localtime.c @@ -1,12 +1,9 @@ -#include +#include "time_impl.h" -#include "__time.h" +struct tm *__localtime_r(const time_t *restrict, struct tm *restrict); struct tm *localtime(const time_t *t) { static struct tm tm; - __tzset(); - __time_to_tm(*t - __timezone, &tm); - tm.tm_isdst = -1; - return __dst_adjust(&tm); + return __localtime_r(t, &tm); } diff --git a/src/time/localtime_r.c b/src/time/localtime_r.c index 389a5917..b36c1d82 100644 --- a/src/time/localtime_r.c +++ b/src/time/localtime_r.c @@ -1,11 +1,15 @@ -#include +#include "time_impl.h" +#include +#include "libc.h" -#include "__time.h" - -struct tm *localtime_r(const time_t *restrict t, struct tm *restrict result) +struct tm *__localtime_r(const time_t *restrict t, struct tm *restrict tm) { - __tzset(); - __time_to_tm(*t - __timezone, result); - result->tm_isdst = -1; - return __dst_adjust(result); + __secs_to_zone(*t, 0, &tm->tm_isdst, &tm->__tm_gmtoff, 0, &tm->__tm_zone); + if (__secs_to_tm((long long)*t - tm->__tm_gmtoff, tm) < 0) { + errno = EINVAL; + return 0; + } + return tm; } + +weak_alias(__localtime_r, localtime_r); diff --git a/src/time/mktime.c b/src/time/mktime.c index 858cd50d..e38b4619 100644 --- a/src/time/mktime.c +++ b/src/time/mktime.c @@ -1,24 +1,30 @@ -#include - -#include "__time.h" +#include "time_impl.h" +#include +#include +#include time_t mktime(struct tm *tm) { - int isdst = tm->tm_isdst; - time_t t, lt; + struct tm new; + long opp; + long long t = __tm_to_secs(tm); + + __secs_to_zone(t, 1, &new.tm_isdst, &new.__tm_gmtoff, &opp, &new.__tm_zone); - __tzset(); + if (tm->tm_isdst>=0 && new.tm_isdst!=tm->tm_isdst) + t += opp - new.__tm_gmtoff; - tm->tm_sec += __timezone; - if (isdst > 0) tm->tm_sec += __dst_offset; + t += new.__tm_gmtoff; + if ((time_t)t != t) goto error; - t = __tm_to_time(tm); - - lt = t - __timezone; - if (isdst > 0) lt -= __dst_offset; - __time_to_tm(lt, tm); + __secs_to_zone(t, 0, &new.tm_isdst, &new.__tm_gmtoff, &opp, &new.__tm_zone); - __dst_adjust(tm); - + if (__secs_to_tm(t - new.__tm_gmtoff, &new) < 0) goto error; + + *tm = new; return t; + +error: + errno = EINVAL; + return -1; } diff --git a/src/time/strftime.c b/src/time/strftime.c index 57687058..d16e8134 100644 --- a/src/time/strftime.c +++ b/src/time/strftime.c @@ -3,7 +3,6 @@ #include #include #include -#include "__time.h" // FIXME: integer overflows @@ -182,14 +181,11 @@ do_fmt: fmt = "%04d"; goto number; case 'z': - if (tm->tm_isdst < 0) continue; - val = -__timezone - (tm->tm_isdst ? __dst_offset : 0); + val = -tm->__tm_gmtoff; l += snprintf(s+l, n-l, "%+.2d%.2d", val/3600, abs(val%3600)/60); continue; case 'Z': - if (tm->tm_isdst < 0 || !__tzname[0] || !__tzname[0][0]) - continue; - l += snprintf(s+l, n-l, "%s", __tzname[!!tm->tm_isdst]); + l += snprintf(s+l, n-l, "%s", tm->__tm_zone); continue; default: return 0; diff --git a/src/time/time_impl.h b/src/time/time_impl.h new file mode 100644 index 00000000..2e9a2c09 --- /dev/null +++ b/src/time/time_impl.h @@ -0,0 +1,9 @@ +#include + +int __days_in_month(int, int); +int __month_to_secs(int, int); +long long __year_to_secs(long long, int *); +long long __tm_to_secs(const struct tm *); +int __secs_to_tm(long long, struct tm *); +void __secs_to_zone(long long, int, int *, long *, long *, const char **); +const unsigned char *__map_file(const char *, size_t *); diff --git a/src/time/timegm.c b/src/time/timegm.c index 6d08917e..7148fca3 100644 --- a/src/time/timegm.c +++ b/src/time/timegm.c @@ -1,9 +1,18 @@ #define _GNU_SOURCE -#include - -#include "__time.h" +#include "time_impl.h" +#include time_t timegm(struct tm *tm) { - return __tm_to_time(tm); + struct tm new; + long long t = __tm_to_secs(tm); + if (__secs_to_tm(t, &new) < 0) { + errno = EINVAL; + return -1; + } + *tm = new; + tm->tm_isdst = 0; + tm->__tm_gmtoff = 0; + tm->__tm_zone = "GMT"; + return t; } diff --git a/src/time/tzset.c b/src/time/tzset.c deleted file mode 100644 index 7e836c2f..00000000 --- a/src/time/tzset.c +++ /dev/null @@ -1,172 +0,0 @@ -#include -#include -#include -#include -#include -#include "libc.h" - -#include "__time.h" - -long __timezone = 0; -int __daylight = 0; -char *__tzname[2] = { 0, 0 }; -int __dst_offset = 0; - -weak_alias(__timezone, timezone); -weak_alias(__daylight, daylight); -weak_alias(__tzname, tzname); - -static char std_name[TZNAME_MAX+1]; -static char dst_name[TZNAME_MAX+1]; - -/* all elements are zero-based */ -static struct rule { - signed char month; - signed char week; - short day; - int time; -} __dst_start, __dst_end; - -static void zname(char *d, char **s) -{ - int i; - for (i=0; i= 0) { - if (**s == ':') ofs += strtol(*s+1, s, 10)*60; - if (**s == ':') ofs += strtol(*s+1, s, 10); - } else { - if (**s == ':') ofs -= strtol(*s+1, s, 10)*60; - if (**s == ':') ofs -= strtol(*s+1, s, 10); - } - return ofs; -} - -static int dstrule(struct rule *rule, char **s) -{ - if (**s != ',') return -1; - switch (*++*s) { - case 'J': - rule->month = 'J'; - rule->day = strtol(*s+1, s, 10)-1; - break; - case 'M': - rule->month = strtol(*s+1, s, 10)-1; - if (**s != '.' || rule->month < 0 || rule->month > 11) - return -1; - rule->week = strtol(*s+1, s, 10)-1; - if (**s != '.' || rule->week < 0 || rule->week > 4) - return -1; - rule->day = strtol(*s+1, s, 10); - if (rule->day < 0 || rule->day > 6) - return -1; - break; - default: - rule->month = 'L'; - rule->day = strtol(*s+1, s, 10); - break; - } - if (**s == '/') { - (*s)++; - rule->time = hhmmss(s); - } else rule->time = 7200; - return 0; -} - -void tzset(void) -{ - char *z, *a; - - strcpy(std_name, "GMT"); - strcpy(dst_name, "GMT"); - __tzname[0] = std_name; - __tzname[1] = dst_name; - __timezone = 0; - __daylight = 0; - - if (!(z = getenv("TZ")) || !isalpha(*z)) return; - - zname(std_name, &z); - __timezone = hhmmss(&z); - - zname(dst_name, &z); - if (dst_name[0]) __daylight=1; - a = z; - __dst_offset = hhmmss(&z) - __timezone; - if (z==a) __dst_offset = -3600; - - if (dstrule(&__dst_start, &z) || dstrule(&__dst_end, &z)) - __daylight = 0; -} - -void __tzset(void) -{ - static int lock[2], init; - if (init) return; - LOCK(lock); - if (!init) tzset(); - init=1; - UNLOCK(lock); -} - -static int is_leap(int year) -{ - year -= 100; - return !(year&3) && ((year%100) || !(year%400)); -} - -static int cutoff_yday(struct tm *tm, struct rule *rule) -{ - static const char days_in_month[] = {31,28,31,30,31,30,31,31,30,31,30,31}; - static const int first_day[] = {0,31,59,90,120,151,181,212,243,273,304,335}; - int yday, mday, leap; - - switch (rule->month) { - case 'J': - return rule->day + (tm->tm_mon > 1 && is_leap(tm->tm_year)); - case 'L': - return rule->day; - default: - yday = first_day[rule->month]; - leap = is_leap(tm->tm_year); - if (rule->month > 1 && leap) yday++; - mday = (rule->day - (yday + tm->tm_wday - tm->tm_yday) + 1400)%7 + 7*rule->week; - if (mday >= days_in_month[rule->month] + (leap && rule->month == 1)) - mday -= 7; - return mday + yday; - } -} - -struct tm *__dst_adjust(struct tm *tm) -{ - time_t t; - int start, end, secs; - int after_start, before_end; - - if (tm->tm_isdst >= 0) return tm; - if (!__daylight) { - tm->tm_isdst = 0; - return tm; - } - - secs = tm->tm_hour*3600 + tm->tm_min*60 + tm->tm_sec; - start = cutoff_yday(tm, &__dst_start); - end = cutoff_yday(tm, &__dst_end); - - after_start = (tm->tm_yday > start || (tm->tm_yday == start && secs >= __dst_start.time)); - before_end = (tm->tm_yday < end || (tm->tm_yday == end && secs < __dst_end.time)); - - if ((after_start && before_end) || ((end < start) && (after_start || before_end))) { - tm->tm_sec -= __dst_offset; - tm->tm_isdst = 1; - t = __tm_to_time(tm); - return __time_to_tm(t, tm); - } else tm->tm_isdst = 0; - - return tm; -} -- 2.20.1 From ac1bf93fc634274c14d234c44b2e846a93a5cc95 Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Wed, 17 Jul 2013 09:29:26 -0400 Subject: [PATCH 08/16] fix fd leak in file mapping code used in new zoneinfo support --- src/time/__map_file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/time/__map_file.c b/src/time/__map_file.c index b6bf272a..b322f095 100644 --- a/src/time/__map_file.c +++ b/src/time/__map_file.c @@ -14,7 +14,7 @@ const char unsigned *__map_file(const char *pathname, size_t *size) if (fd < 0) return 0; if (!__syscall(SYS_fstat, fd, &st)) map = __mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0); - __syscall(SYS_close); + __syscall(SYS_close, fd); *size = st.st_size; return map == MAP_FAILED ? 0 : map; } -- 2.20.1 From ea81c0624f0213430ae8fb959e30373eeaaf391c Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Wed, 17 Jul 2013 10:39:11 -0400 Subject: [PATCH 09/16] fix error code on time conversion overflows POSIX mandates EOVERFLOW for this condition. --- src/time/gmtime_r.c | 2 +- src/time/localtime_r.c | 2 +- src/time/mktime.c | 2 +- src/time/timegm.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/time/gmtime_r.c b/src/time/gmtime_r.c index 0272870d..fee01bd6 100644 --- a/src/time/gmtime_r.c +++ b/src/time/gmtime_r.c @@ -5,7 +5,7 @@ struct tm *__gmtime_r(const time_t *restrict t, struct tm *restrict tm) { if (__secs_to_tm(*t, tm) < 0) { - errno = EINVAL; + errno = EOVERFLOW; return 0; } tm->tm_isdst = 0; diff --git a/src/time/localtime_r.c b/src/time/localtime_r.c index b36c1d82..c52678fd 100644 --- a/src/time/localtime_r.c +++ b/src/time/localtime_r.c @@ -6,7 +6,7 @@ struct tm *__localtime_r(const time_t *restrict t, struct tm *restrict tm) { __secs_to_zone(*t, 0, &tm->tm_isdst, &tm->__tm_gmtoff, 0, &tm->__tm_zone); if (__secs_to_tm((long long)*t - tm->__tm_gmtoff, tm) < 0) { - errno = EINVAL; + errno = EOVERFLOW; return 0; } return tm; diff --git a/src/time/mktime.c b/src/time/mktime.c index e38b4619..ef1fb425 100644 --- a/src/time/mktime.c +++ b/src/time/mktime.c @@ -25,6 +25,6 @@ time_t mktime(struct tm *tm) return t; error: - errno = EINVAL; + errno = EOVERFLOW; return -1; } diff --git a/src/time/timegm.c b/src/time/timegm.c index 7148fca3..e7a7939b 100644 --- a/src/time/timegm.c +++ b/src/time/timegm.c @@ -7,7 +7,7 @@ time_t timegm(struct tm *tm) struct tm new; long long t = __tm_to_secs(tm); if (__secs_to_tm(t, &new) < 0) { - errno = EINVAL; + errno = EOVERFLOW; return -1; } *tm = new; -- 2.20.1 From 3df0b66ee95c837496ae434ba7a2282d97ca71ef Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Wed, 17 Jul 2013 10:43:04 -0400 Subject: [PATCH 10/16] fix missing argument in variadic syscall macros for 0-argument syscalls (1 argument to the macro, the syscall number), the __SYSCALL_NARGS_X macro's ... argument was not satisfied. newer compilers seem to care about this. --- src/internal/syscall.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/internal/syscall.h b/src/internal/syscall.h index 7381efea..8eebe71e 100644 --- a/src/internal/syscall.h +++ b/src/internal/syscall.h @@ -19,7 +19,7 @@ long __syscall_ret(unsigned long), __syscall(long, ...), #define __syscall7(n,a,b,c,d,e,f,g) (__syscall)(n,(long)(a),(long)(b),(long)(c),(long)(d),(long)(e),(long)(f),(long)g) #define __SYSCALL_NARGS_X(a,b,c,d,e,f,g,h,n,...) n -#define __SYSCALL_NARGS(...) __SYSCALL_NARGS_X(__VA_ARGS__,7,6,5,4,3,2,1,0) +#define __SYSCALL_NARGS(...) __SYSCALL_NARGS_X(__VA_ARGS__,7,6,5,4,3,2,1,0,) #define __SYSCALL_CONCAT_X(a,b) a##b #define __SYSCALL_CONCAT(a,b) __SYSCALL_CONCAT_X(a,b) #define __SYSCALL_DISP(b,...) __SYSCALL_CONCAT(b,__SYSCALL_NARGS(__VA_ARGS__))(__VA_ARGS__) -- 2.20.1 From b06dc6663989f8b3c141e439fe89036a44eb7552 Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Wed, 17 Jul 2013 13:54:41 -0400 Subject: [PATCH 11/16] make posix_spawn (and functions that use it) use CLONE_VFORK flag this is both a minor scheduling optimization and a workaround for a difficult-to-fix bug in qemu app-level emulation. from the scheduling standpoint, it makes no sense to schedule the parent thread again until the child has exec'd or exited, since the parent will immediately block again waiting for it. on the qemu side, as regular application code running on an underlying libc, qemu cannot make arbitrary clone syscalls itself without confusing the underlying implementation. instead, it breaks them down into either fork-like or pthread_create-like cases. it was treating the code in posix_spawn as pthread_create-like, due to CLONE_VM, which caused horribly wrong behavior: CLONE_FILES broke the synchronization mechanism, CLONE_SIGHAND broke the parent's signals, and CLONE_THREAD caused the child's exec to end the parent -- if it hadn't already crashed. however, qemu special-cases CLONE_VFORK and emulates that with fork, even when CLONE_VM is also specified. this also gives incorrect semantics for code that really needs the memory sharing, but posix_spawn does not make use of the vm sharing except to avoid momentary double commit charge. programs using posix_spawn (including via popen) should now work correctly under qemu app-level emulation. --- src/process/posix_spawn.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/process/posix_spawn.c b/src/process/posix_spawn.c index e6a031cc..68cf795c 100644 --- a/src/process/posix_spawn.c +++ b/src/process/posix_spawn.c @@ -138,7 +138,8 @@ int __posix_spawnx(pid_t *restrict res, const char *restrict path, args.envp = envp; pthread_sigmask(SIG_BLOCK, SIGALL_SET, &args.oldmask); - pid = __clone(child, stack+sizeof stack, CLONE_VM|SIGCHLD, &args); + pid = __clone(child, stack+sizeof stack, + CLONE_VM|CLONE_VFORK|SIGCHLD, &args); close(args.p[1]); if (pid > 0) { -- 2.20.1 From 2f5fe458085fe8f9d0448aa1ef4eb9143b8b18ca Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Wed, 17 Jul 2013 17:46:38 -0400 Subject: [PATCH 12/16] make spacing in x86_64 alltypes.h.sh more uniform with other archs patch by Luka Perkov, who noted that all other archs have a newline. --- arch/x86_64/bits/alltypes.h.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86_64/bits/alltypes.h.sh b/arch/x86_64/bits/alltypes.h.sh index d3550f56..e5b8d47f 100755 --- a/arch/x86_64/bits/alltypes.h.sh +++ b/arch/x86_64/bits/alltypes.h.sh @@ -19,6 +19,7 @@ union \1 \2;\ TYPEDEF unsigned long size_t; TYPEDEF long ssize_t; TYPEDEF long ptrdiff_t; + TYPEDEF __builtin_va_list va_list; TYPEDEF __builtin_va_list __isoc_va_list; -- 2.20.1 From b823ef2db13a5d86df03bcf6bfd4b92559c0b154 Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Thu, 18 Jul 2013 14:15:48 -0400 Subject: [PATCH 13/16] fix FILENAME_MAX to match PATH_MAX POSIX is not clear on whether it includes the termination, but ISO C requires that it does. the whole concept of this macro is rather useless, but it's better to be correct anyway. --- include/stdio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/stdio.h b/include/stdio.h index fe7ff51d..ad407fb1 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -38,7 +38,7 @@ extern "C" { #define _IONBF 2 #define BUFSIZ 1024 -#define FILENAME_MAX 4095 +#define FILENAME_MAX 4096 #define FOPEN_MAX 1000 #define TMP_MAX 10000 #define L_tmpnam 20 -- 2.20.1 From 23815f88df6c45247f3755dc7857f4013264c04f Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Thu, 18 Jul 2013 19:21:06 -0400 Subject: [PATCH 14/16] fix off-by-one error in checks for implementation-internal signal numbers --- src/signal/sigaddset.c | 2 +- src/signal/sigdelset.c | 2 +- src/signal/sigismember.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/signal/sigaddset.c b/src/signal/sigaddset.c index d632c6fb..edb48d1c 100644 --- a/src/signal/sigaddset.c +++ b/src/signal/sigaddset.c @@ -4,7 +4,7 @@ int sigaddset(sigset_t *set, int sig) { unsigned s = sig-1; - if (s >= 8*sizeof(sigset_t) || s-32U<3) { + if (s >= 8*sizeof(sigset_t) || sig-32U<3) { errno = EINVAL; return -1; } diff --git a/src/signal/sigdelset.c b/src/signal/sigdelset.c index f8794ad6..a77c638f 100644 --- a/src/signal/sigdelset.c +++ b/src/signal/sigdelset.c @@ -4,7 +4,7 @@ int sigdelset(sigset_t *set, int sig) { unsigned s = sig-1; - if (s >= 8*sizeof(sigset_t) || s-32U<3) { + if (s >= 8*sizeof(sigset_t) || sig-32U<3) { errno = EINVAL; return -1; } diff --git a/src/signal/sigismember.c b/src/signal/sigismember.c index d3de6efb..e887b95f 100644 --- a/src/signal/sigismember.c +++ b/src/signal/sigismember.c @@ -4,7 +4,7 @@ int sigismember(const sigset_t *set, int sig) { unsigned s = sig-1; - if (s >= 8*sizeof(sigset_t) || s-32U<3) { + if (s >= 8*sizeof(sigset_t) || sig-32U<3) { errno = EINVAL; return -1; } -- 2.20.1 From f389c4984a0fee80c5322e4d1ec3aa207e9b5c01 Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Thu, 18 Jul 2013 19:29:44 -0400 Subject: [PATCH 15/16] make the dynamic linker find its path file relative to its own location prior to this change, using a non-default syslibdir was impractical on systems where the ordinary library paths contain musl-incompatible library files. the file containing search paths was always taken from /etc, which would either correspond to a system-wide musl installation, or fail to exist at all, resulting in searching of the default library path. the new search strategy is safe even for suid programs because the pathname used comes from the PT_INTERP header of the program being run, rather than any external input. as part of this change, I have also begun differentiating the names of arch variants that differ by endianness or floating point calling convention. the corresponding changes in the build system and and gcc wrapper script (to use an alternate dynamic linker name) for these configurations have not yet been made. --- arch/arm/reloc.h | 15 ++++++++++++++- arch/i386/reloc.h | 2 +- arch/microblaze/reloc.h | 9 ++++++++- arch/mips/reloc.h | 9 ++++++++- arch/powerpc/reloc.h | 2 +- arch/x86_64/reloc.h | 2 +- src/ldso/dynlink.c | 21 ++++++++++++++++++++- 7 files changed, 53 insertions(+), 7 deletions(-) diff --git a/arch/arm/reloc.h b/arch/arm/reloc.h index b41314de..9ca0b48c 100644 --- a/arch/arm/reloc.h +++ b/arch/arm/reloc.h @@ -1,7 +1,20 @@ #include #include +#include -#define ETC_LDSO_PATH "/etc/ld-musl-arm.path" +#if __BYTE_ORDER == __BIG_ENDIAN +#define ENDIAN_SUFFIX "eb" +#else +#define ENDIAN_SUFFIX "" +#endif + +#if __SOFTFP__ +#define FP_SUFFIX "" +#else +#define FP_SUFFIX "hf" +#endif + +#define LDSO_ARCH "arm" ENDIAN_SUFFIX FP_SUFFIX #define IS_COPY(x) ((x)==R_ARM_COPY) #define IS_PLT(x) ((x)==R_ARM_JUMP_SLOT) diff --git a/arch/i386/reloc.h b/arch/i386/reloc.h index da0bc05d..3923b54b 100644 --- a/arch/i386/reloc.h +++ b/arch/i386/reloc.h @@ -1,7 +1,7 @@ #include #include -#define ETC_LDSO_PATH "/etc/ld-musl-i386.path" +#define LDSO_ARCH "i386" #define IS_COPY(x) ((x)==R_386_COPY) #define IS_PLT(x) ((x)==R_386_JMP_SLOT) diff --git a/arch/microblaze/reloc.h b/arch/microblaze/reloc.h index 81add5e9..60f74225 100644 --- a/arch/microblaze/reloc.h +++ b/arch/microblaze/reloc.h @@ -1,7 +1,14 @@ #include #include +#include -#define ETC_LDSO_PATH "/etc/ld-musl-microblaze.path" +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define ENDIAN_SUFFIX "el" +#else +#define ENDIAN_SUFFIX "" +#endif + +#define LDSO_ARCH "microblaze" ENDIAN_SUFFIX #define IS_COPY(x) ((x)==R_MICROBLAZE_COPY) #define IS_PLT(x) ((x)==R_MICROBLAZE_JUMP_SLOT) diff --git a/arch/mips/reloc.h b/arch/mips/reloc.h index f5e9c77b..4c035f32 100644 --- a/arch/mips/reloc.h +++ b/arch/mips/reloc.h @@ -1,7 +1,14 @@ #include #include +#include -#define ETC_LDSO_PATH "/etc/ld-musl-mips.path" +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define ENDIAN_SUFFIX "el" +#else +#define ENDIAN_SUFFIX "" +#endif + +#define LDSO_ARCH "mips" ENDIAN_SUFFIX #define IS_COPY(x) ((x)==R_MIPS_COPY) #define IS_PLT(x) 1 diff --git a/arch/powerpc/reloc.h b/arch/powerpc/reloc.h index 113dfa15..80b9ebc9 100644 --- a/arch/powerpc/reloc.h +++ b/arch/powerpc/reloc.h @@ -1,7 +1,7 @@ #include #include -#define ETC_LDSO_PATH "/etc/ld-musl-powerpc.path" +#define LDSO_ARCH "powerpc" ENDIAN_SUFFIX #define IS_COPY(x) ((x)==R_PPC_COPY) #define IS_PLT(x) ((x)==R_PPC_JMP_SLOT) diff --git a/arch/x86_64/reloc.h b/arch/x86_64/reloc.h index 38a60735..28cf7cc1 100644 --- a/arch/x86_64/reloc.h +++ b/arch/x86_64/reloc.h @@ -2,7 +2,7 @@ #include #include -#define ETC_LDSO_PATH "/etc/ld-musl-x86_64.path" +#define LDSO_ARCH "x86_64" #define IS_COPY(x) ((x)==R_X86_64_COPY) #define IS_PLT(x) ((x)==R_X86_64_JUMP_SLOT) diff --git a/src/ldso/dynlink.c b/src/ldso/dynlink.c index ff5b738d..1d5e2977 100644 --- a/src/ldso/dynlink.c +++ b/src/ldso/dynlink.c @@ -483,7 +483,26 @@ static struct dso *load_library(const char *name) if (fd < 0 && env_path) fd = path_open(name, env_path, buf, sizeof buf); if (fd < 0) { if (!sys_path) { - FILE *f = fopen(ETC_LDSO_PATH, "rbe"); + char *prefix = 0; + size_t prefix_len; + if (ldso->name[0]=='/') { + char *s, *t, *z; + for (s=t=z=ldso->name; *s; s++) + if (*s=='/') z=t, t=s; + prefix_len = z-ldso->name; + if (prefix_len < PATH_MAX) + prefix = ldso->name; + } + if (!prefix) { + prefix = ""; + prefix_len = 0; + } + char etc_ldso_path[prefix_len + 1 + + sizeof "/etc/ld-musl-" LDSO_ARCH ".path"]; + snprintf(etc_ldso_path, sizeof etc_ldso_path, + "%.*s/etc/ld-musl-" LDSO_ARCH ".path", + (int)prefix_len, prefix); + FILE *f = fopen(etc_ldso_path, "rbe"); if (f) { if (getdelim(&sys_path, (size_t[1]){0}, 0, f) <= 0) { free(sys_path); -- 2.20.1 From 3e7f186ea18d7362e3e117f6e848b5514d8266d1 Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Thu, 18 Jul 2013 20:30:58 -0400 Subject: [PATCH 16/16] add build system support for arch endian & float abi variants --- Makefile | 2 +- configure | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6a862110..2fb1c998 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ TOOL_LIBS = lib/musl-gcc.specs ALL_LIBS = $(CRT_LIBS) $(STATIC_LIBS) $(SHARED_LIBS) $(EMPTY_LIBS) $(TOOL_LIBS) ALL_TOOLS = tools/musl-gcc -LDSO_PATHNAME = $(syslibdir)/ld-musl-$(ARCH).so.1 +LDSO_PATHNAME = $(syslibdir)/ld-musl-$(ARCH)$(SUBARCH).so.1 -include config.mak diff --git a/configure b/configure index 96f93b24..042c41bd 100755 --- a/configure +++ b/configure @@ -54,6 +54,20 @@ stripdir () { while eval "fnmatch '*/' \"\${$1}\"" ; do eval "$1=\${$1%/}" ; done } +trycppif () { +printf "checking preprocessor condition %s... " "$1" +echo "#if $1" > "$tmpc" +echo "#error yes" >> "$tmpc" +echo "#endif" >> "$tmpc" +if $CC $2 -c -o /dev/null "$tmpc" >/dev/null 2>&1 ; then +printf "false\n" +return 1 +else +printf "true\n" +return 0 +fi +} + tryflag () { printf "checking whether compiler accepts %s... " "$2" echo "typedef int x;" > "$tmpc" @@ -310,6 +324,23 @@ test -z "$LIBCC" && try_libcc=`$CC -print-file-name=libpcc.a 2>/dev/null` \ && tryldflag LIBCC "$try_libcc" printf "using compiler runtime libraries: %s\n" "$LIBCC" +# Figure out arch variants for archs with variants +SUBARCH= +t="$CFLAGS_C99FSE $CPPFLAGS $CFLAGS_AUTO $CFLAGS" + +if test "$ARCH" = "arm" ; then +trycppif __ARMEB__ "$t" && SUBARCH=${SUBARCH}eb +trycppif __SOFTFP__ "$t" || SUBARCH=${SUBARCH}hf +fi + +test "$ARCH" = "mips" && trycppif "_MIPSEL || __MIPSEL || __MIPSEL__" "$t" \ +&& SUBARCH=${SUBARCH}el + +test "$ARCH" = "microblaze" && trycppif __MICROBLAZEEL__ "$t" \ +&& SUBARCH=${SUBARCH}el + +test "$SUBARCH" \ +&& printf "configured for %s variant: %s\n" "$ARCH" "$ARCH$SUBARCH" printf "creating config.mak... " @@ -320,6 +351,7 @@ cat << EOF # This version of config.mak was generated by configure # Any changes made here will be lost if configure is re-run ARCH = $ARCH +SUBARCH = $SUBARCH prefix = $prefix exec_prefix = $exec_prefix bindir = $bindir -- 2.20.1