[smart] fixup of script execution (#9009)

This patch fixup the script execution capabilities on argv passing
and adds support for arguments larger than 4k.

According to POSIX, the script parameter is quiet different
from the current implementation. Especially on the way it inserts
the path of executables. At the end, when you execute a script
from `$PATH`, it always fails.

For the script, interpreter will be invoked with the following
arguments: `{interpreter [optional-arg] pathname arg...}`
where pathname is the pathname of the file specified as the first
argument of execve(), and arg...  is the series of words pointed
to by the argv argument of execve(), starting at argv[1].  Note that
there is no way to get the argv[0] that was passed to the
execve() call.

The changes include:

- Separating argument, environment variable, and auxiliary vector
  processing into a new lwp_args.c file.
- Fixing bugs in script argument processing and supporting arguments
  larger than 4k.
- Updating lwp_execve to use the new argscopy function and removing
  the old lwp_argscopy function.
- Making various modifications to lwp_load and elf_aux_fill to work
  with the new argument processing.
- Removing unnecessary code related to dynamic loading and interpreter
  scripts.

Signed-off-by: Shell <smokewood@qq.com>
This commit is contained in:
Shell 2024-05-31 17:32:19 +08:00 committed by GitHub
parent 85b428df42
commit e5cf86267b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 940 additions and 1431 deletions

View File

@ -56,8 +56,6 @@
#define O_BINARY 0x10000
#endif
static const char elf_magic[] = {0x7f, 'E', 'L', 'F'};
#ifdef DFS_USING_WORKDIR
extern char working_directory[];
#endif
@ -245,893 +243,6 @@ uint32_t *lwp_get_kernel_sp(void)
#endif
}
#ifdef ARCH_MM_MMU
struct process_aux *lwp_argscopy(struct rt_lwp *lwp, int argc, char **argv, char **envp)
{
int size = sizeof(size_t) * 5; /* store argc, argv, envp, aux, NULL */
int *args;
char *str;
char *str_k;
char **new_argve;
int i;
int len;
size_t *args_k;
struct process_aux *aux;
size_t prot = PROT_READ | PROT_WRITE;
size_t flags = MAP_FIXED | MAP_PRIVATE;
size_t zero = 0;
for (i = 0; i < argc; i++)
{
size += (rt_strlen(argv[i]) + 1);
}
size += (sizeof(size_t) * argc);
i = 0;
if (envp)
{
while (envp[i] != 0)
{
size += (rt_strlen(envp[i]) + 1);
size += sizeof(size_t);
i++;
}
}
/* for aux */
size += sizeof(struct process_aux);
if (size > ARCH_PAGE_SIZE)
{
return RT_NULL;
}
args = lwp_mmap2(lwp, (void *)(USER_STACK_VEND), size, prot, flags, -1, 0);
if (args == RT_NULL || lwp_data_put(lwp, args, &zero, sizeof(zero)) != sizeof(zero))
{
return RT_NULL;
}
args_k = (size_t *)lwp_v2p(lwp, args);
args_k = (size_t *)((size_t)args_k - PV_OFFSET);
/* argc, argv[], 0, envp[], 0 , aux[] */
str = (char *)((size_t)args + (argc + 2 + i + 1 + AUX_ARRAY_ITEMS_NR * 2 + 1) * sizeof(size_t));
str_k = (char *)((size_t)args_k + (argc + 2 + i + 1 + AUX_ARRAY_ITEMS_NR * 2 + 1) * sizeof(size_t));
new_argve = (char **)&args_k[1];
args_k[0] = argc;
for (i = 0; i < argc; i++)
{
len = rt_strlen(argv[i]) + 1;
new_argve[i] = str;
lwp_memcpy(str_k, argv[i], len);
str += len;
str_k += len;
}
new_argve[i] = 0;
i++;
new_argve[i] = 0;
if (envp)
{
int j;
for (j = 0; envp[j] != 0; j++)
{
len = rt_strlen(envp[j]) + 1;
new_argve[i] = str;
lwp_memcpy(str_k, envp[j], len);
str += len;
str_k += len;
i++;
}
new_argve[i] = 0;
}
i++;
/* aux */
aux = (struct process_aux *)(new_argve + i);
aux->item[0].key = AT_EXECFN;
aux->item[0].value = (size_t)(size_t)new_argve[0];
i += AUX_ARRAY_ITEMS_NR * 2;
new_argve[i] = 0;
rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, args_k, size);
lwp->args = args;
return aux;
}
#else
static struct process_aux *lwp_argscopy(struct rt_lwp *lwp, int argc, char **argv, char **envp)
{
#ifdef ARCH_MM_MMU
int size = sizeof(int) * 5; /* store argc, argv, envp, aux, NULL */
struct process_aux *aux;
#else
int size = sizeof(int) * 4; /* store argc, argv, envp, NULL */
#endif /* ARCH_MM_MMU */
int *args;
char *str;
char **new_argve;
int i;
int len;
for (i = 0; i < argc; i++)
{
size += (rt_strlen(argv[i]) + 1);
}
size += (sizeof(int) * argc);
i = 0;
if (envp)
{
while (envp[i] != 0)
{
size += (rt_strlen(envp[i]) + 1);
size += sizeof(int);
i++;
}
}
#ifdef ARCH_MM_MMU
/* for aux */
size += sizeof(struct process_aux);
args = (int *)rt_malloc(size);
if (args == RT_NULL)
{
return RT_NULL;
}
/* argc, argv[], 0, envp[], 0 */
str = (char *)((size_t)args + (argc + 2 + i + 1 + AUX_ARRAY_ITEMS_NR * 2 + 1) * sizeof(int));
#else
args = (int *)rt_malloc(size);
if (args == RT_NULL)
{
return RT_NULL;
}
str = (char*)((int)args + (argc + 2 + i + 1) * sizeof(int));
#endif /* ARCH_MM_MMU */
new_argve = (char **)&args[1];
args[0] = argc;
for (i = 0; i < argc; i++)
{
len = rt_strlen(argv[i]) + 1;
new_argve[i] = str;
lwp_memcpy(str, argv[i], len);
str += len;
}
new_argve[i] = 0;
i++;
new_argve[i] = 0;
if (envp)
{
int j;
for (j = 0; envp[j] != 0; j++)
{
len = rt_strlen(envp[j]) + 1;
new_argve[i] = str;
lwp_memcpy(str, envp[j], len);
str += len;
i++;
}
new_argve[i] = 0;
}
#ifdef ARCH_MM_MMU
/* aux */
aux = (struct process_aux *)(new_argve + i);
aux->item[0].key = AT_EXECFN;
aux->item[0].value = (uint32_t)(size_t)new_argve[0];
i += AUX_ARRAY_ITEMS_NR * 2;
new_argve[i] = 0;
lwp->args = args;
return aux;
#else
lwp->args = args;
lwp->args_length = size;
return (struct process_aux *)(new_argve + i);
#endif /* ARCH_MM_MMU */
}
#endif
#ifdef ARCH_MM_MMU
#define check_off(voff, vlen) \
do \
{ \
if (voff > vlen) \
{ \
result = -RT_ERROR; \
goto _exit; \
} \
} while (0)
#define check_read(vrlen, vrlen_want) \
do \
{ \
if (vrlen < vrlen_want) \
{ \
result = -RT_ERROR; \
goto _exit; \
} \
} while (0)
static size_t load_fread(void *ptr, size_t size, size_t nmemb, int fd)
{
size_t read_block = 0;
while (nmemb)
{
size_t count;
count = read(fd, ptr, size * nmemb) / size;
if (count < nmemb)
{
LOG_E("ERROR: file size error!");
break;
}
ptr = (void *)((uint8_t *)ptr + (count * size));
nmemb -= count;
read_block += count;
}
return read_block;
}
typedef struct
{
Elf_Word st_name;
Elf_Addr st_value;
Elf_Word st_size;
unsigned char st_info;
unsigned char st_other;
Elf_Half st_shndx;
} Elf_sym;
#ifdef ARCH_MM_MMU
struct map_range
{
void *start;
size_t size;
};
static void expand_map_range(struct map_range *m, void *start, size_t size)
{
if (!m->start)
{
m->start = start;
m->size = size;
}
else
{
void *end = (void *)((char*)start + size);
void *mend = (void *)((char*)m->start + m->size);
if (m->start > start)
{
m->start = start;
}
if (mend < end)
{
mend = end;
}
m->size = (char *)mend - (char *)m->start;
}
}
static int map_range_ckeck(struct map_range *m1, struct map_range *m2)
{
void *m1_start = (void *)((size_t)m1->start & ~ARCH_PAGE_MASK);
void *m1_end = (void *)((((size_t)m1->start + m1->size) + ARCH_PAGE_MASK) & ~ARCH_PAGE_MASK);
void *m2_start = (void *)((size_t)m2->start & ~ARCH_PAGE_MASK);
void *m2_end = (void *)((((size_t)m2->start + m2->size) + ARCH_PAGE_MASK) & ~ARCH_PAGE_MASK);
if (m1->size)
{
if (m1_start < (void *)USER_LOAD_VADDR)
{
return -1;
}
if (m1_start > (void *)USER_STACK_VSTART)
{
return -1;
}
if (m1_end < (void *)USER_LOAD_VADDR)
{
return -1;
}
if (m1_end > (void *)USER_STACK_VSTART)
{
return -1;
}
}
if (m2->size)
{
if (m2_start < (void *)USER_LOAD_VADDR)
{
return -1;
}
if (m2_start > (void *)USER_STACK_VSTART)
{
return -1;
}
if (m2_end < (void *)USER_LOAD_VADDR)
{
return -1;
}
if (m2_end > (void *)USER_STACK_VSTART)
{
return -1;
}
}
if ((m1->size != 0) && (m2->size != 0))
{
if (m1_start < m2_start)
{
if (m1_end > m2_start)
{
return -1;
}
}
else /* m2_start <= m1_start */
{
if (m2_end > m1_start)
{
return -1;
}
}
}
return 0;
}
#endif
static int load_elf(int fd, int len, struct rt_lwp *lwp, uint8_t *load_addr, struct process_aux *aux)
{
uint32_t i;
uint32_t off = 0;
size_t load_off = 0;
char *p_section_str = 0;
Elf_sym *dynsym = 0;
Elf_Ehdr eheader;
Elf_Phdr pheader;
Elf_Shdr sheader;
int result = RT_EOK;
uint32_t magic;
size_t read_len;
void *got_start = 0;
size_t got_size = 0;
void *rel_dyn_start = 0;
size_t rel_dyn_size = 0;
size_t dynsym_off = 0;
size_t dynsym_size = 0;
#ifdef ARCH_MM_MMU
struct map_range user_area[2] = {{NULL, 0}, {NULL, 0}}; /* 0 is text, 1 is data */
void *pa, *va;
void *va_self;
#endif
if (len < sizeof eheader)
{
LOG_E("len < sizeof eheader!");
return -RT_ERROR;
}
lseek(fd, 0, SEEK_SET);
read_len = load_fread(&magic, 1, sizeof magic, fd);
check_read(read_len, sizeof magic);
if (memcmp(elf_magic, &magic, 4) != 0)
{
LOG_E("elf_magic not same, magic:0x%x!", magic);
return -RT_ERROR;
}
lseek(fd, off, SEEK_SET);
read_len = load_fread(&eheader, 1, sizeof eheader, fd);
check_read(read_len, sizeof eheader);
#ifndef ARCH_CPU_64BIT
if (eheader.e_ident[4] != 1)
{ /* not 32bit */
LOG_E("elf not 32bit, %d!", eheader.e_ident[4]);
return -RT_ERROR;
}
#else
if (eheader.e_ident[4] != 2)
{ /* not 64bit */
LOG_E("elf not 64bit, %d!", eheader.e_ident[4]);
return -RT_ERROR;
}
#endif
if (eheader.e_ident[6] != 1)
{ /* ver not 1 */
LOG_E("elf Version not 1,ver:%d!", eheader.e_ident[6]);
return -RT_ERROR;
}
if ((eheader.e_type != ET_DYN)
#ifdef ARCH_MM_MMU
&& (eheader.e_type != ET_EXEC)
#endif
)
{
/* not pie or exec elf */
LOG_E("elf type not pie or exec, type:%d!", eheader.e_type);
return -RT_ERROR;
}
#ifdef ARCH_MM_MMU
{
off = eheader.e_phoff;
for (i = 0; i < eheader.e_phnum; i++, off += sizeof pheader)
{
check_off(off, len);
lseek(fd, off, SEEK_SET);
read_len = load_fread(&pheader, 1, sizeof pheader, fd);
check_read(read_len, sizeof pheader);
if (pheader.p_type == PT_DYNAMIC)
{
/* load ld.so */
return 1; /* 1 means dynamic */
}
}
}
#endif
if (eheader.e_entry != 0)
{
if ((eheader.e_entry != USER_LOAD_VADDR)
&& (eheader.e_entry != LDSO_LOAD_VADDR))
{
/* the entry is invalidate */
LOG_E("elf entry is invalidate, entry:0x%x!", eheader.e_entry);
return -RT_ERROR;
}
}
{ /* load aux */
uint8_t *process_header;
size_t process_header_size;
off = eheader.e_phoff;
process_header_size = eheader.e_phnum * sizeof pheader;
#ifdef ARCH_MM_MMU
if (process_header_size > ARCH_PAGE_SIZE - sizeof(char[16]))
{
LOG_E("process_header_size too big, size:0x%x!", process_header_size);
return -RT_ERROR;
}
va = (uint8_t *)lwp_map_user(lwp, (void *)(USER_VADDR_TOP - ARCH_PAGE_SIZE * 2), process_header_size, 0);
if (!va)
{
LOG_E("lwp map user failed!");
return -RT_ERROR;
}
pa = lwp_v2p(lwp, va);
process_header = (uint8_t *)pa - PV_OFFSET;
#else
process_header = (uint8_t *)rt_malloc(process_header_size + sizeof(char[16]));
if (!process_header)
{
LOG_E("process_header malloc failed, size:0x%x!", process_header_size + sizeof(char[16]));
return -RT_ERROR;
}
#endif
check_off(off, len);
lseek(fd, off, SEEK_SET);
read_len = load_fread(process_header, 1, process_header_size, fd);
check_read(read_len, process_header_size);
#ifdef ARCH_MM_MMU
rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, process_header, process_header_size);
#endif
aux->item[1].key = AT_PAGESZ;
#ifdef ARCH_MM_MMU
aux->item[1].value = ARCH_PAGE_SIZE;
#else
aux->item[1].value = RT_MM_PAGE_SIZE;
#endif
aux->item[2].key = AT_RANDOM;
{
uint32_t random_value = rt_tick_get();
uint8_t *random;
#ifdef ARCH_MM_MMU
uint8_t *krandom;
random = (uint8_t *)(USER_VADDR_TOP - ARCH_PAGE_SIZE - sizeof(char[16]));
krandom = (uint8_t *)lwp_v2p(lwp, random);
krandom = (uint8_t *)krandom - PV_OFFSET;
rt_memcpy(krandom, &random_value, sizeof random_value);
#else
random = (uint8_t *)(process_header + process_header_size);
rt_memcpy(random, &random_value, sizeof random_value);
#endif
aux->item[2].value = (size_t)random;
}
aux->item[3].key = AT_PHDR;
#ifdef ARCH_MM_MMU
aux->item[3].value = (size_t)va;
#else
aux->item[3].value = (size_t)process_header;
#endif
aux->item[4].key = AT_PHNUM;
aux->item[4].value = eheader.e_phnum;
aux->item[5].key = AT_PHENT;
aux->item[5].value = sizeof pheader;
#ifdef ARCH_MM_MMU
rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, aux, sizeof *aux);
#endif
}
if (load_addr)
{
load_off = (size_t)load_addr;
}
#ifdef ARCH_MM_MMU
else
{
/* map user */
off = eheader.e_shoff;
for (i = 0; i < eheader.e_shnum; i++, off += sizeof sheader)
{
check_off(off, len);
lseek(fd, off, SEEK_SET);
read_len = load_fread(&sheader, 1, sizeof sheader, fd);
check_read(read_len, sizeof sheader);
if ((sheader.sh_flags & SHF_ALLOC) == 0)
{
continue;
}
switch (sheader.sh_type)
{
case SHT_PROGBITS:
if ((sheader.sh_flags & SHF_WRITE) == 0)
{
expand_map_range(&user_area[0], (void *)sheader.sh_addr, sheader.sh_size);
}
else
{
expand_map_range(&user_area[1], (void *)sheader.sh_addr, sheader.sh_size);
}
break;
case SHT_NOBITS:
expand_map_range(&user_area[1], (void *)sheader.sh_addr, sheader.sh_size);
break;
default:
expand_map_range(&user_area[1], (void *)sheader.sh_addr, sheader.sh_size);
break;
}
}
if (user_area[0].size == 0)
{
/* no code */
result = -RT_ERROR;
goto _exit;
}
if (user_area[0].start == NULL)
{
/* DYN */
load_off = USER_LOAD_VADDR;
user_area[0].start = (void *)((char*)user_area[0].start + load_off);
user_area[1].start = (void *)((char*)user_area[1].start + load_off);
}
if (map_range_ckeck(&user_area[0], &user_area[1]) != 0)
{
result = -RT_ERROR;
goto _exit;
}
/* text and data */
for (i = 0; i < 2; i++)
{
if (user_area[i].size != 0)
{
va = lwp_map_user(lwp, user_area[i].start, user_area[i].size, (i == 0));
if (!va || (va != user_area[i].start))
{
result = -RT_ERROR;
goto _exit;
}
}
}
lwp->text_size = user_area[0].size;
}
#else
else
{
size_t start = -1UL;
size_t end = 0UL;
size_t total_size;
off = eheader.e_shoff;
for (i = 0; i < eheader.e_shnum; i++, off += sizeof sheader)
{
check_off(off, len);
lseek(fd, off, SEEK_SET);
read_len = load_fread(&sheader, 1, sizeof sheader, fd);
check_read(read_len, sizeof sheader);
if ((sheader.sh_flags & SHF_ALLOC) == 0)
{
continue;
}
switch (sheader.sh_type)
{
case SHT_PROGBITS:
case SHT_NOBITS:
if (start > sheader.sh_addr)
{
start = sheader.sh_addr;
}
if (sheader.sh_addr + sheader.sh_size > end)
{
end = sheader.sh_addr + sheader.sh_size;
}
break;
default:
break;
}
}
total_size = end - start;
#ifdef RT_USING_CACHE
load_off = (size_t)rt_malloc_align(total_size, RT_CPU_CACHE_LINE_SZ);
#else
load_off = (size_t)rt_malloc(total_size);
#endif
if (load_off == 0)
{
LOG_E("alloc text memory faild!");
result = -RT_ENOMEM;
goto _exit;
}
else
{
LOG_D("lwp text malloc : %p, size: %d!", (void *)load_off, lwp->text_size);
}
lwp->load_off = load_off; /* for free */
lwp->text_size = total_size;
}
#endif
lwp->text_entry = (void *)(eheader.e_entry + load_off);
off = eheader.e_phoff;
for (i = 0; i < eheader.e_phnum; i++, off += sizeof pheader)
{
check_off(off, len);
lseek(fd, off, SEEK_SET);
read_len = load_fread(&pheader, 1, sizeof pheader, fd);
check_read(read_len, sizeof pheader);
if (pheader.p_type == PT_LOAD)
{
if (pheader.p_filesz > pheader.p_memsz)
{
LOG_E("pheader.p_filesz > pheader.p_memsz, p_filesz:0x%x;p_memsz:0x%x!", pheader.p_filesz, pheader.p_memsz);
return -RT_ERROR;
}
check_off(pheader.p_offset, len);
lseek(fd, pheader.p_offset, SEEK_SET);
#ifdef ARCH_MM_MMU
{
uint32_t size = pheader.p_filesz;
size_t tmp_len = 0;
va = (void *)(pheader.p_vaddr + load_addr);
read_len = 0;
while (size)
{
pa = lwp_v2p(lwp, va);
va_self = (void *)((char *)pa - PV_OFFSET);
LOG_D("va_self = %p pa = %p", va_self, pa);
tmp_len = (size < ARCH_PAGE_SIZE) ? size : ARCH_PAGE_SIZE;
tmp_len = load_fread(va_self, 1, tmp_len, fd);
rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, va_self, tmp_len);
read_len += tmp_len;
size -= tmp_len;
va = (void *)((char *)va + ARCH_PAGE_SIZE);
}
}
#else
read_len = load_fread((void*)(pheader.p_vaddr + load_off), 1, pheader.p_filesz, fd);
#endif
check_read(read_len, pheader.p_filesz);
if (pheader.p_filesz < pheader.p_memsz)
{
#ifdef ARCH_MM_MMU
uint32_t size = pheader.p_memsz - pheader.p_filesz;
uint32_t size_s;
uint32_t off;
off = pheader.p_filesz & ARCH_PAGE_MASK;
va = (void *)((pheader.p_vaddr + pheader.p_filesz + load_off) & ~ARCH_PAGE_MASK);
while (size)
{
size_s = (size < ARCH_PAGE_SIZE - off) ? size : ARCH_PAGE_SIZE - off;
pa = lwp_v2p(lwp, va);
va_self = (void *)((char *)pa - PV_OFFSET);
memset((void *)((char *)va_self + off), 0, size_s);
rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, (void *)((char *)va_self + off), size_s);
off = 0;
size -= size_s;
va = (void *)((char *)va + ARCH_PAGE_SIZE);
}
#else
memset((uint8_t *)pheader.p_vaddr + pheader.p_filesz + load_off, 0, (size_t)(pheader.p_memsz - pheader.p_filesz));
#endif
}
}
}
/* relocate */
if (eheader.e_type == ET_DYN)
{
/* section info */
off = eheader.e_shoff;
/* find section string table */
check_off(off, len);
lseek(fd, off + (sizeof sheader) * eheader.e_shstrndx, SEEK_SET);
read_len = load_fread(&sheader, 1, sizeof sheader, fd);
check_read(read_len, sizeof sheader);
p_section_str = (char *)rt_malloc(sheader.sh_size);
if (!p_section_str)
{
LOG_E("out of memory!");
result = -ENOMEM;
goto _exit;
}
check_off(sheader.sh_offset, len);
lseek(fd, sheader.sh_offset, SEEK_SET);
read_len = load_fread(p_section_str, 1, sheader.sh_size, fd);
check_read(read_len, sheader.sh_size);
check_off(off, len);
lseek(fd, off, SEEK_SET);
for (i = 0; i < eheader.e_shnum; i++, off += sizeof sheader)
{
read_len = load_fread(&sheader, 1, sizeof sheader, fd);
check_read(read_len, sizeof sheader);
if (strcmp(p_section_str + sheader.sh_name, ".got") == 0)
{
got_start = (void *)((uint8_t *)sheader.sh_addr + load_off);
got_size = (size_t)sheader.sh_size;
}
else if (strcmp(p_section_str + sheader.sh_name, ".rel.dyn") == 0)
{
rel_dyn_start = (void *)((uint8_t *)sheader.sh_addr + load_off);
rel_dyn_size = (size_t)sheader.sh_size;
}
else if (strcmp(p_section_str + sheader.sh_name, ".dynsym") == 0)
{
dynsym_off = (size_t)sheader.sh_offset;
dynsym_size = (size_t)sheader.sh_size;
}
}
/* reloc */
if (dynsym_size)
{
dynsym = rt_malloc(dynsym_size);
if (!dynsym)
{
LOG_E("ERROR: Malloc error!");
result = -ENOMEM;
goto _exit;
}
check_off(dynsym_off, len);
lseek(fd, dynsym_off, SEEK_SET);
read_len = load_fread(dynsym, 1, dynsym_size, fd);
check_read(read_len, dynsym_size);
}
#ifdef ARCH_MM_MMU
arch_elf_reloc(lwp->aspace, (void *)load_off, rel_dyn_start, rel_dyn_size, got_start, got_size, dynsym);
#else
arch_elf_reloc((void *)load_off, rel_dyn_start, rel_dyn_size, got_start, got_size, dynsym);
rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, lwp->text_entry, lwp->text_size);
rt_hw_cpu_icache_ops(RT_HW_CACHE_INVALIDATE, lwp->text_entry, lwp->text_size);
#endif
}
LOG_D("lwp->text_entry = 0x%p", lwp->text_entry);
LOG_D("lwp->text_size = 0x%p", lwp->text_size);
_exit:
if (dynsym)
{
rt_free(dynsym);
}
if (p_section_str)
{
rt_free(p_section_str);
}
if (result != RT_EOK)
{
LOG_E("lwp load faild, %d", result);
}
return result;
}
#endif /* ARCH_MM_MMU */
rt_weak int lwp_load(const char *filename, struct rt_lwp *lwp, uint8_t *load_addr, size_t addr_size, struct process_aux *aux)
{
uint8_t *ptr;
int ret = -1;
int len;
int fd = -1;
/* check file name */
RT_ASSERT(filename != RT_NULL);
/* check lwp control block */
RT_ASSERT(lwp != RT_NULL);
/* copy file name to process name */
rt_strncpy(lwp->cmd, filename, RT_NAME_MAX);
if (load_addr != RT_NULL)
{
lwp->lwp_type = LWP_TYPE_FIX_ADDR;
ptr = load_addr;
}
else
{
lwp->lwp_type = LWP_TYPE_DYN_ADDR;
ptr = RT_NULL;
}
fd = open(filename, O_BINARY | O_RDONLY, 0);
if (fd < 0)
{
LOG_E("ERROR: Can't open elf file %s!", filename);
goto out;
}
len = lseek(fd, 0, SEEK_END);
if (len < 0)
{
LOG_E("ERROR: File %s size error!", filename);
goto out;
}
lseek(fd, 0, SEEK_SET);
ret = load_elf(fd, len, lwp, ptr, aux);
if ((ret != RT_EOK) && (ret != 1))
{
LOG_E("lwp load ret = %d", ret);
}
out:
if (fd > 0)
{
close(fd);
}
return ret;
}
/* lwp-thread clean up routine */
void lwp_cleanup(struct rt_thread *tid)
@ -1286,6 +397,34 @@ rt_err_t lwp_children_unregister(struct rt_lwp *parent, struct rt_lwp *child)
return 0;
}
struct process_aux *argscopy(struct rt_lwp *lwp, int argc, char **argv, char **envp)
{
struct lwp_args_info ai;
rt_err_t error;
struct process_aux *ua;
const char **tail_argv[2] = {0};
error = lwp_args_init(&ai);
if (error)
{
return RT_NULL;
}
if (argc > 0)
{
tail_argv[0] = (void *)argv[argc - 1];
argv[argc - 1] = NULL;
lwp_args_put(&ai, (void *)argv, LWP_ARGS_TYPE_KARG);
lwp_args_put(&ai, (void *)tail_argv, LWP_ARGS_TYPE_KARG);
}
lwp_args_put(&ai, (void *)envp, LWP_ARGS_TYPE_KENVP);
ua = lwp_argscopy(lwp, &ai);
lwp_args_detach(&ai);
return ua;
}
pid_t lwp_execve(char *filename, int debug, int argc, char **argv, char **envp)
{
int result;
@ -1327,7 +466,7 @@ pid_t lwp_execve(char *filename, int debug, int argc, char **argv, char **envp)
}
#endif
if ((aux = lwp_argscopy(lwp, argc, argv, envp)) == RT_NULL)
if ((aux = argscopy(lwp, argc, argv, envp)) == RT_NULL)
{
lwp_tid_put(tid);
lwp_ref_dec(lwp);
@ -1335,14 +474,6 @@ pid_t lwp_execve(char *filename, int debug, int argc, char **argv, char **envp)
}
result = lwp_load(filename, lwp, RT_NULL, 0, aux);
#ifdef ARCH_MM_MMU
if (result == 1)
{
/* dynmaic */
lwp_unmap_user(lwp, (void *)(USER_VADDR_TOP - ARCH_PAGE_SIZE));
result = load_ldso(lwp, filename, argv, envp);
}
#endif /* ARCH_MM_MMU */
if (result == RT_EOK)
{
rt_thread_t thread = RT_NULL;

View File

@ -32,6 +32,7 @@
#include "lwp_signal.h"
#include "lwp_syscall.h"
#include "lwp_avl.h"
#include "lwp_args.h"
#include "mm_aspace.h"
#ifdef RT_USING_MUSLLIBC
@ -237,6 +238,8 @@ void lwp_tid_dec_ref(rt_thread_t thread);
void lwp_tid_set_thread(int tid, rt_thread_t thread);
int lwp_execve(char *filename, int debug, int argc, char **argv, char **envp);
int lwp_load(const char *filename, struct rt_lwp *lwp, uint8_t *load_addr, size_t addr_size, struct process_aux *aux);
void lwp_user_obj_free(struct rt_lwp *lwp);
/*create by lwp_setsid.c*/
int setsid(void);
@ -382,15 +385,6 @@ struct process_aux
struct process_aux_item item[AUX_ARRAY_ITEMS_NR];
};
struct lwp_args_info
{
char **argv;
char **envp;
int argc;
int envc;
int size;
};
struct dbg_ops_t
{
int (*dbg)(int argc, char **argv);

781
components/lwp/lwp_args.c Normal file
View File

@ -0,0 +1,781 @@
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-01-12 Shell separate argv, envp, aux processing to lwp_args.c
* Bugs fix for script arguments processing.
* support args larger than 4k
*/
#include "lwp_args.h"
#include "lwp_internal.h"
#include "mm_page.h"
static void _strvec_init(struct lwp_string_vector *sv)
{
#define DEFAUTL_ARGV_BUFLEN 4
sv->strvec = rt_malloc(DEFAUTL_ARGV_BUFLEN * sizeof(char *));
sv->strvec_buflen = DEFAUTL_ARGV_BUFLEN;
sv->string_count = 0;
}
static void _strvec_detach(struct lwp_string_vector *sv)
{
if (sv->strvec)
{
rt_free(sv->strvec);
}
}
static rt_err_t _strvec_append(struct lwp_string_vector *sv, const char *string)
{
if (sv->string_count == sv->strvec_buflen)
{
void *newptr;
newptr = rt_realloc(sv->strvec, sv->strvec_buflen * 2 * sizeof(char *));
if (!newptr)
return -RT_ENOMEM;
sv->strvec = newptr;
sv->strvec_buflen *= 2;
}
sv->strvec[sv->string_count++] = string;
return RT_EOK;
}
static rt_err_t args_append(struct lwp_args_info *ai, const char *str_addr,
size_t str_len, enum lwp_args_type atype)
{
rt_err_t error;
char *str_bufaddr;
if (ai->strings_length + str_len + 1 > ai->str_buf_size)
{
/* reallocate buffer for this */
void *newptr;
newptr = rt_realloc(ai->str_buf, ai->str_buf_size * 2);
if (!newptr)
return -RT_ENOMEM;
ai->str_buf = newptr;
ai->str_buf_size *= 2;
}
/* append new string to string buffer and update strings_length */
str_bufaddr = &ai->str_buf[ai->strings_length];
if (atype == LWP_ARGS_TYPE_KARG || atype == LWP_ARGS_TYPE_KENVP)
{
strcpy(str_bufaddr, str_addr);
ai->strings_length += str_len + 1;
}
else
{
lwp_get_from_user(str_bufaddr, (void *)str_addr, str_len);
ai->strings_length += str_len;
ai->str_buf[ai->strings_length++] = '\0';
}
/* append new argument or environment */
switch (atype)
{
case LWP_ARGS_TYPE_ARG:
case LWP_ARGS_TYPE_KARG:
error = _strvec_append(&ai->argv, str_bufaddr);
if (!error && ai->argv.string_count == 1)
{
ai->argv0_strlen = str_len;
}
break;
case LWP_ARGS_TYPE_ENVP:
case LWP_ARGS_TYPE_KENVP:
error = _strvec_append(&ai->envp, str_bufaddr);
break;
default:
break;
}
return error;
}
/**
* @brief Override arguments 0 for script interpreter.
*
* Manual: interpreter will be invoked with the following arguments:
* {interpreter [optional-arg] pathname arg...}
* where pathname is the pathname of the file specified as the first
* argument of execve(), and arg... is the series of words pointed
* to by the argv argument of execve(), starting at argv[1]. Note
* that there is no way to get the argv[0] that was passed to the
* execve() call.
*/
static rt_err_t _args_override_argv0(struct lwp_args_info *ai, struct lwp_args_info *ow_ai)
{
rt_err_t error = 0;
int i, new_argc, new_strbuf_size, ai_bytes_tobe_copied;
char **new_argv, *new_strbuf, *base;
rt_base_t off;
if (ow_ai == 0 || ow_ai->argv.string_count == 0)
{
return -RT_EINVAL;
}
/* for new argument vector */
new_argc = ai->argv.string_count - 1 + ow_ai->argv.string_count;
new_argv = rt_malloc(new_argc * sizeof(char *));
if (!new_argv)
{
return -RT_ENOMEM;
}
/* for new string buffer */
ai_bytes_tobe_copied = ai->strings_length - (ai->argv0_strlen + 1);
new_strbuf_size = ai_bytes_tobe_copied + ow_ai->strings_length;
new_strbuf = rt_malloc(new_strbuf_size);
if (!new_argv)
{
rt_free(new_argv);
return -RT_ENOMEM;
}
base = new_strbuf;
off = base - ow_ai->str_buf;
/* copy overriding argument strings and argv */
memcpy(base, ow_ai->str_buf, ow_ai->strings_length);
for (i = 0; i < ow_ai->argv.string_count; i++)
{
/* base + ow_ai->argv.strvec[i] - ow_ai->str_buf */
new_argv[i] = (char *)ow_ai->argv.strvec[i] + off;
}
base += ow_ai->strings_length;
off = base - (ai->str_buf + ai->argv0_strlen + 1);
/* copy old argument strings starting from argv[1] and setup new_argv */
memcpy(base, ai->str_buf + ai->argv0_strlen + 1, ai_bytes_tobe_copied);
for (size_t j = 1; j < ai->argv.string_count; i++, j++)
{
/* base + ai->argv->strvec[j] - ai->str_buf */
new_argv[i] = (char *)ai->argv.strvec[j] + off;
}
/* setup envp for ai */
for (i = 0; i < ai->envp.string_count; i++)
{
/* base + ai->envp->strvec[i] - ai->str_buf */
ai->envp.strvec[i] += off;
}
/* replace strings buffer and argv buffer */
ai->str_buf = new_strbuf;
ai->strings_length = new_strbuf_size;
ai->str_buf_size = new_strbuf_size;
ai->argv.string_count = new_argc;
ai->argv.strvec = (void *)new_argv;
ai->argv.strvec_buflen = new_argc;
ai->argv0_strlen = ow_ai->argv0_strlen;
return error;
}
const char *lwp_args_get_argv_0(struct lwp_args_info *ai)
{
return ai->str_buf;
}
static rt_err_t args_init(struct lwp_args_info *ai, size_t str_buf_size)
{
void *str_buf;
str_buf = rt_malloc(str_buf_size);
if (!str_buf)
return -RT_ENOMEM;
memset(ai, 0, sizeof(*ai));
_strvec_init(&ai->argv);
if (!ai->argv.strvec)
{
rt_free(str_buf);
return -RT_ENOMEM;
}
_strvec_init(&ai->envp);
if (!ai->envp.strvec)
{
rt_free(str_buf);
_strvec_detach(&ai->argv);
return -RT_ENOMEM;
}
ai->str_buf_size = str_buf_size;
ai->str_buf = str_buf;
return RT_EOK;
}
#define STR_BUF_DEFAULT_SIZE 2048
rt_err_t lwp_args_init(struct lwp_args_info *ai)
{
return args_init(ai, STR_BUF_DEFAULT_SIZE);
}
void lwp_args_detach(struct lwp_args_info *ai)
{
_strvec_detach(&ai->argv);
_strvec_detach(&ai->envp);
rt_free(ai->str_buf);
}
#ifdef ARCH_MM_MMU
struct process_aux *lwp_argscopy(struct rt_lwp *lwp, struct lwp_args_info *ai)
{
int size = sizeof(rt_base_t) * 4; /* store argc, argv_NULL, envp_NULL, aux_NULL */
char *str_ua;
const char **args_ua;
const char **iter;
rt_base_t off;
struct process_aux_item pa_item;
struct process_aux *aux_ua;
size_t prot = PROT_READ | PROT_WRITE;
size_t flags = MAP_FIXED | MAP_PRIVATE;
rt_base_t argc = ai->argv.string_count;
rt_base_t envc = ai->envp.string_count;
/**
* counts the bytes to storage the args
*/
size += argc * sizeof(char *) + envc * sizeof(char *)
+ ai->strings_length + sizeof(struct process_aux);
args_ua = lwp_mmap2(lwp, (void *)(USER_STACK_VEND), size, prot, flags, -1, 0);
if (args_ua == RT_NULL)
{
return RT_NULL;
}
/**
* @brief Put data from args info to user space
* argc, argv[], NULL, envp[], NULL, aux[], NULL, strings
*/
iter = args_ua;
/* argc */
lwp_data_put(lwp, iter++, &argc, sizeof(char *));
/* strings */
str_ua = (char *)((rt_ubase_t)args_ua +
(1 + argc + 1 + envc + 1 + AUX_ARRAY_ITEMS_NR * 2 + 1) * sizeof(rt_base_t));
lwp_data_put(lwp, str_ua, ai->str_buf, ai->strings_length);
/* argv */
off = str_ua - ai->str_buf;
for (size_t i = 0; i < argc; i++)
{
/* str_ua + ai->argv.strvec[i] - ai->str_buf */
ai->argv.strvec[i] += off;
}
lwp_data_put(lwp, iter, ai->argv.strvec, sizeof(char *) * ai->argv.string_count);
iter += ai->argv.string_count;
/* NULL */
lwp_data_set(lwp, iter++, 0, sizeof(char *));
/* envp */
for (size_t i = 0; i < envc; i++)
{
/* str_ua + ai->envp.strvec[i] - ai->str_buf */
ai->envp.strvec[i] += off;
}
lwp_data_put(lwp, iter, ai->envp.strvec, sizeof(char *) * ai->envp.string_count);
iter += ai->envp.string_count;
/* NULL */
lwp_data_set(lwp, iter++, 0, sizeof(char *));
/* aux */
aux_ua = (struct process_aux *)iter;
pa_item.key = AT_EXECFN;
pa_item.value = (size_t)str_ua;
lwp_data_put(lwp, iter, &pa_item, sizeof(pa_item));
iter += AUX_ARRAY_ITEMS_NR * 2;
/* NULL */
lwp_data_set(lwp, iter++, 0, sizeof(char *));
lwp->args = args_ua;
return aux_ua;
}
#else
static struct process_aux *lwp_argscopy(struct rt_lwp *lwp, int argc, char **argv, char **envp)
{
#ifdef ARCH_MM_MMU
int size = sizeof(int) * 5; /* store argc, argv, envp, aux, NULL */
struct process_aux *aux;
#else
int size = sizeof(int) * 4; /* store argc, argv, envp, NULL */
#endif /* ARCH_MM_MMU */
int *args;
char *str;
char **new_argve;
int i;
int len;
for (i = 0; i < argc; i++)
{
size += (rt_strlen(argv[i]) + 1);
}
size += (sizeof(int) * argc);
i = 0;
if (envp)
{
while (envp[i] != 0)
{
size += (rt_strlen(envp[i]) + 1);
size += sizeof(int);
i++;
}
}
#ifdef ARCH_MM_MMU
/* for aux */
size += sizeof(struct process_aux);
args = (int *)rt_malloc(size);
if (args == RT_NULL)
{
return RT_NULL;
}
/* argc, argv[], 0, envp[], 0 */
str = (char *)((size_t)args + (argc + 2 + i + 1 + AUX_ARRAY_ITEMS_NR * 2 + 1) * sizeof(int));
#else
args = (int *)rt_malloc(size);
if (args == RT_NULL)
{
return RT_NULL;
}
str = (char*)((int)args + (argc + 2 + i + 1) * sizeof(int));
#endif /* ARCH_MM_MMU */
new_argve = (char **)&args[1];
args[0] = argc;
for (i = 0; i < argc; i++)
{
len = rt_strlen(argv[i]) + 1;
new_argve[i] = str;
lwp_memcpy(str, argv[i], len);
str += len;
}
new_argve[i] = 0;
i++;
new_argve[i] = 0;
if (envp)
{
int j;
for (j = 0; envp[j] != 0; j++)
{
len = rt_strlen(envp[j]) + 1;
new_argve[i] = str;
lwp_memcpy(str, envp[j], len);
str += len;
i++;
}
new_argve[i] = 0;
}
#ifdef ARCH_MM_MMU
/* aux */
aux = (struct process_aux *)(new_argve + i);
aux->item[0].key = AT_EXECFN;
aux->item[0].value = (uint32_t)(size_t)new_argve[0];
i += AUX_ARRAY_ITEMS_NR * 2;
new_argve[i] = 0;
lwp->args = args;
return aux;
#else
lwp->args = args;
lwp->args_length = size;
return (struct process_aux *)(new_argve + i);
#endif /* ARCH_MM_MMU */
}
#endif
rt_err_t lwp_args_put(struct lwp_args_info *args, const char **strv_addr, enum lwp_args_type atype)
{
rt_err_t error;
int iter = 0;
int len;
const char *arg_ptr;
while (1)
{
if (atype == LWP_ARGS_TYPE_ARG || atype == LWP_ARGS_TYPE_ENVP)
{
len = lwp_get_from_user(&arg_ptr, strv_addr + iter++, sizeof(char *));
if (len != sizeof(char *))
{
return -EFAULT;
}
if (arg_ptr == NULL)
{
break;
}
len = lwp_user_strlen(arg_ptr);
if (len < 0)
{
return -EFAULT;
}
}
else
{
arg_ptr = strv_addr[iter++];
if (arg_ptr == NULL)
{
break;
}
len = strlen(arg_ptr);
}
error = args_append(args, arg_ptr, len, atype);
if (error)
{
return error;
}
}
return 0;
}
/**
* @brief Put argument vector to args object
*/
rt_err_t lwp_args_put_argv(struct lwp_args_info *args, const char **argv_uaddr)
{
return lwp_args_put(args, argv_uaddr, LWP_ARGS_TYPE_ARG);
}
/**
* @brief Put argument vector to args object
*/
rt_err_t lwp_args_put_envp(struct lwp_args_info *args, const char **envp_uaddr)
{
return lwp_args_put(args, envp_uaddr, LWP_ARGS_TYPE_ENVP);
}
/**
* read words until reach nextline or EOF.
* words copied into buffer is never truncated.
*/
#define READFILE_STAT_EOF_REACHED 0
#define READFILE_STAT_NEXTLINE_REACHED 0
#define READFILE_STAT_TRUNCATED 1
#define READFILE_STAT_CAN_READMORE(stat) (stat)
static int _readfile(int fd, size_t maxbytes, char *buffer, int *p_readlen)
{
int readlen;
int stat;
char *nlp;
readlen = read(fd, buffer, maxbytes - 1);
if (readlen <= 0)
{
/* eof, failed */
stat = READFILE_STAT_EOF_REACHED;
buffer[0] = '\0';
}
else
{
if ((nlp = strchr(buffer, '\n')) == NULL)
{
if (readlen == maxbytes - 1)
{
int tailing_wordlen = 0;
char *cp = buffer + readlen - 1;
for (; *cp && *cp != ' ' && *cp != '\t'; cp--, tailing_wordlen++)
;
if (tailing_wordlen)
{
lseek(fd, -tailing_wordlen, SEEK_CUR);
readlen -= tailing_wordlen;
stat = READFILE_STAT_TRUNCATED;
}
else
{
stat = READFILE_STAT_EOF_REACHED;
}
}
else
{
stat = READFILE_STAT_EOF_REACHED;
}
}
else
{
stat = READFILE_STAT_NEXTLINE_REACHED;
readlen = nlp - buffer;
}
buffer[readlen] = '\0';
}
if (p_readlen)
*p_readlen = readlen;
return stat;
}
static char *_find_word(char *cp)
{
for (; (*cp == ' ') || (*cp == '\t'); cp++)
;
return cp;
}
static char *_seperate_and_get_nextword(char *cp)
{
/* find next whitespace */
for (; *cp && (*cp != ' ') && (*cp != '\t'); cp++)
;
/* seperate words */
while ((*cp == ' ') || (*cp == '\t'))
{
*cp++ = '\0';
}
return cp;
}
#define INTERP_BUF_SIZE 128
rt_err_t lwp_args_load_script(struct lwp_args_info *ai, const char *filename)
{
rt_err_t error = -1;
int fd = -RT_ERROR;
int len;
int rf_stat;
char interp[INTERP_BUF_SIZE];
char *cp, *nextword;
char script_magic[2];
struct lwp_args_info ow_ai = {0};
fd = open(filename, O_BINARY | O_RDONLY, 0);
if (fd < 0)
{
goto quit;
}
/**
* verify an interpreter script by matching script file magic
* eg: #!/bin/sh
*/
len = read(fd, script_magic, sizeof(script_magic));
if (len != 2 || memcmp(script_magic, "#!", sizeof(script_magic)))
{
goto quit;
}
/* setup a new args struct to save script arguments */
if (args_init(&ow_ai, INTERP_BUF_SIZE))
{
goto quit;
}
while (1)
{
/* read file to buffer (avoid any truncated words in buffer) */
rf_stat = _readfile(fd, INTERP_BUF_SIZE, interp, &len);
if (len <= 0)
{
goto quit;
}
/* find first word until reaching nil */
cp = _find_word(interp);
if (*cp == '\0')
{
if (READFILE_STAT_CAN_READMORE(rf_stat))
continue;
else
break;
}
do
{
nextword = _seperate_and_get_nextword(cp);
args_append(&ow_ai, cp, strlen(cp), LWP_ARGS_TYPE_KARG);
cp = nextword;
}
while (*cp);
if (READFILE_STAT_CAN_READMORE(rf_stat))
continue;
else
break;
}
if (ow_ai.argv.string_count == 0)
{
goto quit; /* No interpreter name found */
}
args_append(&ow_ai, filename, strlen(filename), LWP_ARGS_TYPE_KARG);
error = _args_override_argv0(ai, &ow_ai);
if (error)
{
goto quit;
}
quit:
lwp_args_detach(&ow_ai);
if (fd >= 0)
{
close(fd);
}
return error;
}
char** lwp_get_command_line_args(struct rt_lwp *lwp)
{
size_t argc = 0;
char** argv = NULL;
int ret;
size_t i;
size_t len;
if (lwp)
{
ret = lwp_data_get(lwp, &argc, lwp->args, sizeof(argc));
if (ret == 0)
{
return RT_NULL;
}
argv = (char**)rt_malloc((argc + 1) * sizeof(char*));
if (argv)
{
for (i = 0; i < argc; i++)
{
char *argvp = NULL;
ret = lwp_data_get(lwp, &argvp, &((char **)lwp->args)[1 + i], sizeof(argvp));
if (ret == 0)
{
lwp_free_command_line_args(argv);
return RT_NULL;
}
len = lwp_user_strlen_ext(lwp, argvp);
if (len > 0)
{
argv[i] = (char*)rt_malloc(len + 1);
ret = lwp_data_get(lwp, argv[i], argvp, len);
if (ret == 0)
{
lwp_free_command_line_args(argv);
return RT_NULL;
}
argv[i][len] = '\0';
}
else
{
argv[i] = NULL;
}
}
argv[argc] = NULL;
}
}
return argv;
}
void lwp_print_envp(struct rt_lwp *lwp)
{
rt_size_t envp_counts;
char **kenvp_array = lwp_get_envp(lwp, &envp_counts);
if (kenvp_array)
{
rt_kprintf("envp_counts: %d\n", envp_counts);
for (size_t i = 0; i < envp_counts; i++)
{
rt_kprintf("envp[%d]: %s\n", i, kenvp_array[i]);
}
}
lwp_free_command_line_args(kenvp_array);
return ;
}
char** lwp_get_envp(struct rt_lwp *lwp, rt_size_t *penvp_counts)
{
int ret, len;
rt_base_t argc;
char **p_kenvp = RT_NULL;
char *envp, **p_envp;
size_t envp_counts = 0;
if (lwp)
{
ret = lwp_data_get(lwp, &argc, lwp->args, sizeof(argc));
if (ret == 0)
{
return RT_NULL;
}
p_envp = (char **)lwp->args + 1 + argc + 1;
/* counts envp */
while (lwp_data_get(lwp, &envp, p_envp, sizeof(void *)) == sizeof(void *)
&& envp != NULL)
{
p_envp++;
envp_counts++;
}
p_kenvp = (char **)rt_malloc((envp_counts + 1) * sizeof(char *));
if (p_kenvp)
{
/* copy env from envp array */
p_envp = (char **)lwp->args + 1 + argc + 1;
for (size_t i = 0; i < envp_counts; i++)
{
ret = lwp_data_get(lwp, &envp, &p_envp[i], sizeof(char *));
if (ret != sizeof(char **))
{
lwp_free_command_line_args(p_kenvp);
return RT_NULL;
}
len = lwp_user_strlen_ext(lwp, envp);
if (len > 0)
{
p_kenvp[i] = (char*)rt_malloc(len + 1);
ret = lwp_data_get(lwp, p_kenvp[i], envp, len + 1);
if (ret != len + 1)
{
lwp_free_command_line_args(p_kenvp);
return RT_NULL;
}
}
else
{
p_kenvp[i] = NULL;
}
}
if (penvp_counts)
*penvp_counts = envp_counts;
p_kenvp[envp_counts] = NULL;
}
}
return p_kenvp;
}
void lwp_free_command_line_args(char** argv)
{
size_t i;
if (argv)
{
for (i = 0; argv[i]; i++)
{
rt_free(argv[i]);
}
rt_free(argv);
}
}

58
components/lwp/lwp_args.h Normal file
View File

@ -0,0 +1,58 @@
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-01-12 Shell separate argv, envp, aux processing from execve(2).
* Bugs fix for script arguments processing.
*/
#ifndef __LWP_ARGV_H__
#define __LWP_ARGV_H__
#include <rtthread.h>
struct rt_lwp;
enum lwp_args_type {
LWP_ARGS_TYPE_ARG,
LWP_ARGS_TYPE_KARG,
LWP_ARGS_TYPE_ENVP,
LWP_ARGS_TYPE_KENVP,
LWP_ARGS_TYPE_NULLPTR
};
struct lwp_string_vector
{
const char **strvec;
rt_uint32_t strvec_buflen;
rt_uint32_t string_count;
};
struct lwp_args_info
{
int argv0_strlen;
int strings_length;
int str_buf_size;
char *str_buf;
struct lwp_string_vector argv;
struct lwp_string_vector envp;
};
rt_err_t lwp_args_init(struct lwp_args_info *ai);
void lwp_args_detach(struct lwp_args_info *ai);
struct process_aux *lwp_argscopy(struct rt_lwp *lwp, struct lwp_args_info *args_info);;
rt_err_t lwp_args_put(struct lwp_args_info *args, const char **strv_addr, enum lwp_args_type atype);
rt_err_t lwp_args_put_argv(struct lwp_args_info *args, const char **argv_uaddr);
rt_err_t lwp_args_put_envp(struct lwp_args_info *args, const char **envp_uaddr);
rt_err_t lwp_args_load_script(struct lwp_args_info *args, const char *filename);
const char *lwp_args_get_argv_0(struct lwp_args_info *ai);
char** lwp_get_envp(struct rt_lwp *lwp, rt_size_t *penvp_counts);
void lwp_print_envp(struct rt_lwp *lwp);
char** lwp_get_command_line_args(struct rt_lwp *lwp);
void lwp_free_command_line_args(char** argv);
#endif /* __LWP_ARGV_H__ */

View File

@ -50,8 +50,10 @@
#define ELF_AUX_ENT(aux, id, val) \
do \
{ \
*aux++ = id; \
*aux++ = val; \
rt_base_t a = id; \
lwp_data_put(lwp, aux++, &a, sizeof(rt_ubase_t)); \
a = val; \
lwp_data_put(lwp, aux++, &a, sizeof(rt_ubase_t)); \
} while (0)
typedef struct
@ -571,6 +573,7 @@ static int elf_aux_fill(elf_load_info_t *load_info)
uint32_t random_value = rt_tick_get();
size_t prot = PROT_READ | PROT_WRITE;
size_t flags = MAP_PRIVATE;
rt_lwp_t lwp = load_info->lwp;
void *va;
if (!aux)
@ -581,7 +584,7 @@ static int elf_aux_fill(elf_load_info_t *load_info)
aux_info = (elf_addr_t *)aux->item;
ELF_AUX_ENT(aux_info, AT_PAGESZ, ARCH_PAGE_SIZE);
va = lwp_mmap2(load_info->lwp, (void *)(USER_VADDR_TOP - ARCH_PAGE_SIZE * 2), ARCH_PAGE_SIZE, prot, flags, -1, 0);
va = lwp_mmap2(lwp, (void *)(USER_VADDR_TOP - ARCH_PAGE_SIZE * 2), ARCH_PAGE_SIZE, prot, flags, -1, 0);
if (!va)
{
LOG_E("lwp map user failed!");
@ -604,10 +607,6 @@ static int elf_aux_fill(elf_load_info_t *load_info)
ELF_AUX_ENT(aux_info, AT_CLKTCK, 0);
ELF_AUX_ENT(aux_info, AT_SECURE, 0);
#ifdef ARCH_MM_MMU
rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, aux, sizeof(*aux));
#endif
return 0;
}
@ -757,7 +756,7 @@ OUT:
}
int lwp_load(const char *filename, struct rt_lwp *lwp, uint8_t *load_addr, size_t addr_size,
struct process_aux *aux)
struct process_aux *aux_ua)
{
elf_load_info_t load_info = { 0 };
int len;
@ -789,7 +788,7 @@ int lwp_load(const char *filename, struct rt_lwp *lwp, uint8_t *load_addr, size_
}
load_info.lwp = lwp;
load_info.aux = aux;
load_info.aux = aux_ua;
load_info.exec_info.fd = ELF_INVALID_FD;
load_info.interp_info.fd = ELF_INVALID_FD;

View File

@ -186,6 +186,4 @@ rt_err_t lwp_critical_exit(struct rt_lwp *lwp);
#define LWP_RETURN(name) {RT_ASSERT(name != _LWP_UNINITIALIZED_RC);return name;}
#endif /* LWP_DEBUG */
int load_ldso(struct rt_lwp *lwp, char *exec_name, char *const argv[], char *const envp[]);
#endif /* __LWP_INTERNAL_H__ */

View File

@ -2162,10 +2162,6 @@ rt_weak sysret_t sys_vfork(void)
return sys_fork();
}
struct process_aux *lwp_argscopy(struct rt_lwp *lwp, int argc, char **argv, char **envp);
int lwp_load(const char *filename, struct rt_lwp *lwp, uint8_t *load_addr, size_t addr_size, struct process_aux *aux);
void lwp_user_obj_free(struct rt_lwp *lwp);
#define _swap_lwp_data(lwp_used, lwp_new, type, member) \
do {\
type tmp;\
@ -2174,339 +2170,17 @@ void lwp_user_obj_free(struct rt_lwp *lwp);
lwp_new->member = tmp;\
} while (0)
static char *_insert_args(int new_argc, char *new_argv[], struct lwp_args_info *args)
{
void *page = NULL;
int err = 0;
char **nargv;
char **nenvp;
char *p;
int i, len;
int nsize;
if (new_argc == 0)
{
goto quit;
}
page = rt_pages_alloc_ext(0, PAGE_ANY_AVAILABLE); /* 1 page */
if (!page)
{
goto quit;
}
nsize = new_argc * sizeof(char *);
for (i = 0; i < new_argc; i++)
{
nsize += lwp_strlen(lwp_self(), new_argv[i]) + 1;
}
if (nsize + args->size > ARCH_PAGE_SIZE)
{
err = 1;
goto quit;
}
nargv = (char **)page;
nenvp = nargv + args->argc + new_argc + 1;
p = (char *)(nenvp + args->envc + 1);
/* insert argv */
for (i = 0; i < new_argc; i++)
{
nargv[i] = p;
len = lwp_strlen(lwp_self(), new_argv[i]) + 1;
lwp_memcpy(p, new_argv[i], len);
p += len;
}
/* copy argv */
nargv += new_argc;
for (i = 0; i < args->argc; i++)
{
nargv[i] = p;
len = lwp_strlen(lwp_self(), args->argv[i]) + 1;
lwp_memcpy(p, args->argv[i], len);
p += len;
}
nargv[i] = NULL;
/* copy envp */
for (i = 0; i < args->envc; i++)
{
nenvp[i] = p;
len = lwp_strlen(lwp_self(), args->envp[i]) + 1;
lwp_memcpy(p, args->envp[i], len);
p += len;
}
nenvp[i] = NULL;
/* update args */
args->argv = (char **)page;
args->argc = args->argc + new_argc;
args->envp = args->argv + args->argc + 1;
/* args->envc no change */
args->size = args->size + nsize;
quit:
if (err && page)
{
rt_pages_free(page, 0);
page = NULL;
}
return page;
}
#define INTERP_BUF_SIZE 128
static char *_load_script(const char *filename, void *old_page, struct lwp_args_info *args)
{
char *new_page = NULL;
int fd = -RT_ERROR;
int len;
char interp[INTERP_BUF_SIZE];
char *cp;
char *i_name;
char *i_arg;
fd = open(filename, O_BINARY | O_RDONLY, 0);
if (fd < 0)
{
goto quit;
}
len = read(fd, interp, INTERP_BUF_SIZE);
if (len < 2)
{
goto quit;
}
/*
* match find file header the first line.
* eg: #!/bin/sh
*/
if ((interp[0] != '#') || (interp[1] != '!'))
{
goto quit;
}
if (len == INTERP_BUF_SIZE)
{
len--;
}
interp[len] = '\0';
if ((cp = strchr(interp, '\n')) == NULL)
{
cp = interp + INTERP_BUF_SIZE - 1;
}
*cp = '\0';
while (cp > interp)
{
cp--;
if ((*cp == ' ') || (*cp == '\t'))
{
*cp = '\0';
}
else
{
break;
}
}
for (cp = interp + 2; (*cp == ' ') || (*cp == '\t'); cp++)
{
/* nothing */
}
if (*cp == '\0')
{
goto quit; /* No interpreter name found */
}
i_name = cp;
i_arg = NULL;
for (; *cp && (*cp != ' ') && (*cp != '\t'); cp++)
{
/* nothing */
}
while ((*cp == ' ') || (*cp == '\t'))
{
*cp++ = '\0';
}
if (*cp)
{
i_arg = cp;
}
if (i_arg)
{
new_page = _insert_args(1, &i_arg, args);
if (!new_page)
{
goto quit;
}
rt_pages_free(old_page, 0);
old_page = new_page;
}
new_page = _insert_args(1, &i_name, args);
if (!new_page)
{
goto quit;
}
rt_pages_free(old_page, 0);
quit:
if (fd >= 0)
{
close(fd);
}
return new_page;
}
int load_ldso(struct rt_lwp *lwp, char *exec_name, char *const argv[], char *const envp[])
{
int ret = -1;
int i;
void *page;
void *new_page;
int argc = 0;
int envc = 0;
int size;
char **kargv;
char **kenvp;
size_t len;
char *p;
char *i_arg;
struct lwp_args_info args_info;
struct process_aux *aux;
size = sizeof(char *);
if (argv)
{
while (1)
{
if (!argv[argc])
{
break;
}
len = lwp_strlen(lwp, (const char *)argv[argc]);
size += sizeof(char *) + len + 1;
argc++;
}
}
if (envp)
{
while (1)
{
if (!envp[envc])
{
break;
}
len = lwp_strlen(lwp, (const char *)envp[envc]);
size += sizeof(char *) + len + 1;
envc++;
}
}
page = rt_pages_alloc_ext(0, PAGE_ANY_AVAILABLE); /* 1 page */
if (!page)
{
SET_ERRNO(ENOMEM);
goto quit;
}
kargv = (char **)page;
kenvp = kargv + argc + 1;
p = (char *)(kenvp + envc + 1);
/* copy argv */
if (argv)
{
for (i = 0; i < argc; i++)
{
kargv[i] = p;
len = lwp_strlen(lwp, argv[i]) + 1;
lwp_memcpy(p, argv[i], len);
p += len;
}
kargv[i] = NULL;
}
/* copy envp */
if (envp)
{
for (i = 0; i < envc; i++)
{
kenvp[i] = p;
len = lwp_strlen(lwp, envp[i]) + 1;
lwp_memcpy(p, envp[i], len);
p += len;
}
kenvp[i] = NULL;
}
args_info.argc = argc;
args_info.argv = kargv;
args_info.envc = envc;
args_info.envp = kenvp;
args_info.size = size;
new_page = _insert_args(1, &exec_name, &args_info);
if (!new_page)
{
SET_ERRNO(ENOMEM);
goto quit;
}
rt_pages_free(page, 0);
page = new_page;
i_arg = "-e";
new_page = _insert_args(1, &i_arg, &args_info);
if (!new_page)
{
SET_ERRNO(ENOMEM);
goto quit;
}
rt_pages_free(page, 0);
page = new_page;
i_arg = "ld.so";
new_page = _insert_args(1, &i_arg, &args_info);
if (!new_page)
{
SET_ERRNO(ENOMEM);
goto quit;
}
rt_pages_free(page, 0);
page = new_page;
if ((aux = lwp_argscopy(lwp, args_info.argc, args_info.argv, args_info.envp)) == NULL)
{
SET_ERRNO(ENOMEM);
goto quit;
}
ret = lwp_load("/lib/ld.so", lwp, RT_NULL, 0, aux);
rt_strncpy(lwp->cmd, exec_name, RT_NAME_MAX - 1);
quit:
if (page)
{
rt_pages_free(page, 0);
}
return (ret < 0 ? GET_ERRNO() : ret);
}
sysret_t sys_execve(const char *path, char *const argv[], char *const envp[])
{
int ret = -1;
int argc = 0;
int envc = 0;
void *page = NULL;
void *new_page;
int size = 0;
int error = -1;
size_t len;
char **kargv;
char **kenvp;
char *p;
struct rt_lwp *new_lwp = NULL;
struct rt_lwp *lwp;
int uni_thread;
rt_thread_t thread;
struct process_aux *aux;
int i;
struct lwp_args_info args_info;
if (access(path, X_OK) != 0)
{
return -EACCES;
}
char *kpath = RT_NULL;
lwp = lwp_self();
thread = rt_thread_self();
@ -2525,163 +2199,108 @@ sysret_t sys_execve(const char *path, char *const argv[], char *const envp[])
if (!uni_thread)
{
SET_ERRNO(EINVAL);
goto quit;
return -EINVAL;
}
len = lwp_user_strlen(path);
if (len <= 0)
{
SET_ERRNO(EFAULT);
goto quit;
return -EFAULT;
}
size += sizeof(char *);
if (argv)
kpath = rt_malloc(len + 1);
if (!kpath)
{
while (1)
{
if (!lwp_user_accessable((void *)(argv + argc), sizeof(char *)))
{
SET_ERRNO(EFAULT);
goto quit;
}
if (!argv[argc])
{
break;
}
len = lwp_user_strlen((const char *)argv[argc]);
if (len < 0)
{
SET_ERRNO(EFAULT);
goto quit;
}
size += sizeof(char *) + len + 1;
argc++;
}
}
size += sizeof(char *);
if (envp)
{
while (1)
{
if (!lwp_user_accessable((void *)(envp + envc), sizeof(char *)))
{
SET_ERRNO(EFAULT);
goto quit;
}
if (!envp[envc])
{
break;
}
len = lwp_user_strlen((const char *)envp[envc]);
if (len < 0)
{
SET_ERRNO(EFAULT);
goto quit;
}
size += sizeof(char *) + len + 1;
envc++;
}
}
if (size > ARCH_PAGE_SIZE)
{
SET_ERRNO(EINVAL);
goto quit;
}
page = rt_pages_alloc_ext(0, PAGE_ANY_AVAILABLE); /* 1 page */
if (!page)
{
SET_ERRNO(ENOMEM);
goto quit;
return -ENOMEM;
}
if (lwp_get_from_user(kpath, (void *)path, len) != len)
{
rt_free(kpath);
return -EFAULT;
}
kpath[len] = '\0';
if (access(kpath, X_OK) != 0)
{
rt_free(kpath);
return -EACCES;
}
/* setup args */
error = lwp_args_init(&args_info);
if (error)
{
rt_free(kpath);
return -ENOMEM;
}
kargv = (char **)page;
kenvp = kargv + argc + 1;
p = (char *)(kenvp + envc + 1);
/* copy argv */
if (argv)
{
for (i = 0; i < argc; i++)
error = lwp_args_put_argv(&args_info, (void *)argv);
if (error)
{
kargv[i] = p;
len = lwp_user_strlen(argv[i]) + 1;
lwp_memcpy(p, argv[i], len);
p += len;
error = -EFAULT;
goto quit;
}
kargv[i] = NULL;
}
/* copy envp */
if (envp)
{
for (i = 0; i < envc; i++)
error = lwp_args_put_envp(&args_info, (void *)envp);
if (error)
{
kenvp[i] = p;
len = lwp_user_strlen(envp[i]) + 1;
lwp_memcpy(p, envp[i], len);
p += len;
error = -EFAULT;
goto quit;
}
kenvp[i] = NULL;
}
/* alloc new lwp to operation */
new_lwp = lwp_create(LWP_CREATE_FLAG_NONE);
if (!new_lwp)
{
SET_ERRNO(ENOMEM);
error = -ENOMEM;
goto quit;
}
ret = lwp_user_space_init(new_lwp, 0);
if (ret != 0)
error = lwp_user_space_init(new_lwp, 0);
if (error != 0)
{
SET_ERRNO(ENOMEM);
error = -ENOMEM;
goto quit;
}
/* file is a script ? */
args_info.argc = argc;
args_info.argv = kargv;
args_info.envc = envc;
args_info.envp = kenvp;
args_info.size = size;
path = kpath;
while (1)
{
new_page = _load_script(path, page, &args_info);
if (!new_page)
error = lwp_args_load_script(&args_info, path);
if (error != 0)
{
break;
}
page = new_page;
path = args_info.argv[0];
path = lwp_args_get_argv_0(&args_info);
}
/* now load elf */
if ((aux = lwp_argscopy(new_lwp, args_info.argc, args_info.argv, args_info.envp)) == NULL)
if ((aux = lwp_argscopy(new_lwp, &args_info)) == NULL)
{
SET_ERRNO(ENOMEM);
error = -ENOMEM;
goto quit;
}
ret = lwp_load(path, new_lwp, RT_NULL, 0, aux);
if (ret == 1)
{
/* dynamic */
lwp_unmap_user(new_lwp, (void *)(USER_VADDR_TOP - ARCH_PAGE_SIZE));
ret = load_ldso(new_lwp, (char *)path, args_info.argv, args_info.envp);
}
if (ret == RT_EOK)
error = lwp_load(path, new_lwp, RT_NULL, 0, aux);
if (error == RT_EOK)
{
int off = 0;
int last_backslash = 0;
char *run_name = args_info.argv[0];
/* clear all user objects */
lwp_user_object_clear(lwp);
/* find last \ or / */
/* find last \ or / to get base name */
while (1)
{
char c = run_name[off++];
char c = path[off++];
if (c == '\0')
{
@ -2700,13 +2319,11 @@ sysret_t sys_execve(const char *path, char *const argv[], char *const envp[])
*/
RT_ASSERT(rt_list_entry(lwp->t_grp.prev, struct rt_thread, sibling) == thread);
strncpy(thread->parent.name, run_name + last_backslash, RT_NAME_MAX - 1);
strncpy(thread->parent.name, path + last_backslash, RT_NAME_MAX - 1);
strncpy(lwp->cmd, new_lwp->cmd, RT_NAME_MAX);
rt_free(lwp->exe_file);
lwp->exe_file = strndup(new_lwp->exe_file, DFS_PATH_MAX);
rt_pages_free(page, 0);
#ifdef ARCH_MM_MMU
_swap_lwp_data(lwp, new_lwp, struct rt_aspace *, aspace);
@ -2737,17 +2354,18 @@ sysret_t sys_execve(const char *path, char *const argv[], char *const envp[])
(char *)thread->stack_addr + thread->stack_size);
/* never reach here */
}
return -EINVAL;
error = -EINVAL;
quit:
if (page)
if (kpath)
{
rt_pages_free(page, 0);
rt_free(kpath);
}
lwp_args_detach(&args_info);
if (new_lwp)
{
lwp_ref_dec(new_lwp);
}
return (ret < 0 ? GET_ERRNO() : ret);
return error;
}
#endif /* ARCH_MM_MMU */

View File

@ -991,71 +991,4 @@ size_t lwp_strlen(struct rt_lwp *lwp, const char *s)
return strlen(s);
}
char** lwp_get_command_line_args(struct rt_lwp *lwp)
{
size_t argc = 0;
char** argv = NULL;
int ret;
size_t i;
size_t len;
if (lwp)
{
ret = lwp_data_get(lwp, &argc, lwp->args, sizeof(argc));
if (ret == 0)
{
return RT_NULL;
}
argv = (char**)rt_malloc((argc + 1) * sizeof(char*));
if (argv)
{
for (i = 0; i < argc; i++)
{
char *argvp = NULL;
ret = lwp_data_get(lwp, &argvp, &((char **)lwp->args)[1 + i], sizeof(argvp));
if (ret == 0)
{
lwp_free_command_line_args(argv);
return RT_NULL;
}
len = lwp_user_strlen_ext(lwp, argvp);
if (len > 0)
{
argv[i] = (char*)rt_malloc(len + 1);
ret = lwp_data_get(lwp, argv[i], argvp, len);
if (ret == 0)
{
lwp_free_command_line_args(argv);
return RT_NULL;
}
argv[i][len] = '\0';
}
else
{
argv[i] = NULL;
}
}
argv[argc] = NULL;
}
}
return argv;
}
void lwp_free_command_line_args(char** argv)
{
size_t i;
if (argv)
{
for (i = 0; argv[i]; i++)
{
rt_free(argv[i]);
}
rt_free(argv);
}
}
#endif

View File

@ -151,9 +151,6 @@ void lwp_unmap_user_space(struct rt_lwp *lwp);
int lwp_unmap_user(struct rt_lwp *lwp, void *va);
void *lwp_map_user(struct rt_lwp *lwp, void *map_va, size_t map_size, rt_bool_t text);
void lwp_free_command_line_args(char** argv);
char** lwp_get_command_line_args(struct rt_lwp *lwp);
rt_varea_t lwp_map_user_varea(struct rt_lwp *lwp, void *map_va, size_t map_size);
/* check LWP_MAP_FLAG_* */