Fix snooping of 16-bit dlls being loaded at the same address.
[wine] / library / ldt.c
1 /*
2  * LDT manipulation functions
3  *
4  * Copyright 1993 Robert J. Amstadt
5  * Copyright 1995 Alexandre Julliard
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <errno.h>
29
30 #include "winbase.h"
31 #include "wine/library.h"
32
33 #ifdef __i386__
34
35 #ifdef linux
36
37 #ifdef HAVE_SYS_SYSCALL_H
38 # include <sys/syscall.h>
39 #endif
40
41 struct modify_ldt_s
42 {
43     unsigned int  entry_number;
44     unsigned long base_addr;
45     unsigned int  limit;
46     unsigned int  seg_32bit : 1;
47     unsigned int  contents : 2;
48     unsigned int  read_exec_only : 1;
49     unsigned int  limit_in_pages : 1;
50     unsigned int  seg_not_present : 1;
51 };
52
53 static inline int modify_ldt( int func, struct modify_ldt_s *ptr,
54                                   unsigned long count )
55 {
56     int res;
57 #ifdef __PIC__
58     __asm__ __volatile__( "pushl %%ebx\n\t"
59                           "movl %2,%%ebx\n\t"
60                           "int $0x80\n\t"
61                           "popl %%ebx"
62                           : "=a" (res)
63                           : "0" (SYS_modify_ldt),
64                             "r" (func),
65                             "c" (ptr),
66                             "d" (count) );
67 #else
68     __asm__ __volatile__("int $0x80"
69                          : "=a" (res)
70                          : "0" (SYS_modify_ldt),
71                            "b" (func),
72                            "c" (ptr),
73                            "d" (count) );
74 #endif  /* __PIC__ */
75     if (res >= 0) return res;
76     errno = -res;
77     return -1;
78 }
79
80 #endif  /* linux */
81
82 #if defined(__svr4__) || defined(_SCO_DS)
83 #include <sys/sysi86.h>
84 extern int sysi86(int,void*);
85 #ifndef __sun__
86 #include <sys/seg.h>
87 #endif
88 #endif
89
90 #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
91 #include <machine/segments.h>
92
93 extern int i386_get_ldt(int, union descriptor *, int);
94 extern int i386_set_ldt(int, union descriptor *, int);
95 #endif  /* __NetBSD__ || __FreeBSD__ || __OpenBSD__ */
96
97 #endif  /* __i386__ */
98
99 /* local copy of the LDT */
100 struct __wine_ldt_copy wine_ldt_copy;
101
102
103 /***********************************************************************
104  *           ldt_get_entry
105  *
106  * Retrieve an LDT entry.
107  */
108 void wine_ldt_get_entry( unsigned short sel, LDT_ENTRY *entry )
109 {
110     int index = sel >> 3;
111     wine_ldt_set_base(  entry, wine_ldt_copy.base[index] );
112     wine_ldt_set_limit( entry, wine_ldt_copy.limit[index] );
113     wine_ldt_set_flags( entry, wine_ldt_copy.flags[index] );
114 }
115
116
117 /***********************************************************************
118  *           ldt_set_entry
119  *
120  * Set an LDT entry.
121  */
122 int wine_ldt_set_entry( unsigned short sel, const LDT_ENTRY *entry )
123 {
124     int ret = 0, index = sel >> 3;
125
126     /* Entry 0 must not be modified; its base and limit are always 0 */
127     if (!index) return 0;
128
129 #ifdef __i386__
130
131 #ifdef linux
132     {
133         struct modify_ldt_s ldt_info;
134
135         ldt_info.entry_number    = index;
136         ldt_info.base_addr       = (unsigned long)wine_ldt_get_base(entry);
137         ldt_info.limit           = entry->LimitLow | (entry->HighWord.Bits.LimitHi << 16);
138         ldt_info.seg_32bit       = entry->HighWord.Bits.Default_Big;
139         ldt_info.contents        = (entry->HighWord.Bits.Type >> 2) & 3;
140         ldt_info.read_exec_only  = !(entry->HighWord.Bits.Type & 2);
141         ldt_info.limit_in_pages  = entry->HighWord.Bits.Granularity;
142         ldt_info.seg_not_present = !entry->HighWord.Bits.Pres;
143
144         if ((ret = modify_ldt(1, &ldt_info, sizeof(ldt_info))) < 0)
145             perror( "modify_ldt" );
146     }
147 #endif  /* linux */
148
149 #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
150     {
151         LDT_ENTRY entry_copy = *entry;
152         /* The kernel will only let us set LDTs with user priority level */
153         if (entry_copy.HighWord.Bits.Pres
154             && entry_copy.HighWord.Bits.Dpl != 3)
155                 entry_copy.HighWord.Bits.Dpl = 3;
156         ret = i386_set_ldt(index, (union descriptor *)&entry_copy, 1);
157         if (ret < 0)
158         {
159             perror("i386_set_ldt");
160             fprintf( stderr, "Did you reconfigure the kernel with \"options USER_LDT\"?\n" );
161             exit(1);
162         }
163     }
164 #endif  /* __NetBSD__ || __FreeBSD__ || __OpenBSD__ */
165
166 #if defined(__svr4__) || defined(_SCO_DS)
167     {
168         struct ssd ldt_mod;
169         ldt_mod.sel  = sel;
170         ldt_mod.bo   = (unsigned long)wine_ldt_get_base(entry);
171         ldt_mod.ls   = entry->LimitLow | (entry->HighWord.Bits.LimitHi << 16);
172         ldt_mod.acc1 = entry->HighWord.Bytes.Flags1;
173         ldt_mod.acc2 = entry->HighWord.Bytes.Flags2 >> 4;
174         if ((ret = sysi86(SI86DSCR, &ldt_mod)) == -1) perror("sysi86");
175     }
176 #endif
177
178 #endif  /* __i386__ */
179
180     if (ret >= 0)
181     {
182         wine_ldt_copy.base[index]  = wine_ldt_get_base(entry);
183         wine_ldt_copy.limit[index] = wine_ldt_get_limit(entry);
184         wine_ldt_copy.flags[index] = (entry->HighWord.Bits.Type |
185                                  (entry->HighWord.Bits.Default_Big ? WINE_LDT_FLAGS_32BIT : 0) |
186                                  (wine_ldt_copy.flags[index] & WINE_LDT_FLAGS_ALLOCATED));
187     }
188     return ret;
189 }
190
191
192 /***********************************************************************
193  *           selector access functions
194  */
195 #ifdef __i386__
196 # ifndef _MSC_VER
197 /* Nothing needs to be done for MS C, it will do with inline versions from the winnt.h */
198 __ASM_GLOBAL_FUNC( wine_get_cs, "movw %cs,%ax\n\tret" )
199 __ASM_GLOBAL_FUNC( wine_get_ds, "movw %ds,%ax\n\tret" )
200 __ASM_GLOBAL_FUNC( wine_get_es, "movw %es,%ax\n\tret" )
201 __ASM_GLOBAL_FUNC( wine_get_fs, "movw %fs,%ax\n\tret" )
202 __ASM_GLOBAL_FUNC( wine_get_gs, "movw %gs,%ax\n\tret" )
203 __ASM_GLOBAL_FUNC( wine_get_ss, "movw %ss,%ax\n\tret" )
204 __ASM_GLOBAL_FUNC( wine_set_fs, "movl 4(%esp),%eax\n\tmovw %ax,%fs\n\tret" )
205 __ASM_GLOBAL_FUNC( wine_set_gs, "movl 4(%esp),%eax\n\tmovw %ax,%gs\n\tret" )
206 # endif /* defined(_MSC_VER) */
207 #endif /* defined(__i386__) */